inside the c++ object笔记(一)

本文详细探讨了C++中的对象模型,包括struct与class的区别,对象内存布局,以及非静态和静态成员的位置。此外,还讲解了C++的默认构造函数、拷贝构造函数的合成条件和作用,以及初始化参数的不同方法。文章强调了在某些情况下避免自定义拷贝构造函数以优化性能,并提到了初始化列表的注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

chapter1

struct

struct声明,class定义只会警告,实际由定义的关键字决定

 

对象

对象之中的变量在内存中的顺序为:

同access section中按照声明顺序,不同的access section按照section写的顺序

 

c++对象模型

对象内:

non-static data members(非静态数据成员)                      放在类内

static data member(静态数据成员)                                   放在类外独立一块(独立在任何对象外)

non-static func                                                                       类外独立一块

static func                                                                              类外独立一块

vptr -> vptbl -> virtual func                                                     vptr在类内

 

若数据复杂,则可以把数据成员分离出来作为一个struct

在旧的c中,由class继承数据struct

现在的做法是把struct的指针/引用/对象放在class的private中

 

三种模型

程序模型:数据函数分离

 

抽象模型ADT

单一对象加上接口

 

面向对象模型

基类分配统一接口给所有的派生类

 

 

class大小计算

non-static data member的大小

virtual函数的vptr

virtual base class 的 virtual base class pointer

齐位造成的补足空间(可能补在成员间可能补在对象边界)

 

void*

可以指向任何对象,但是无法执行任何操作

 

多态

引用与指针时实现多态机制的根本

 

在派生类赋给(初始化给)基类的“对象”时,编译器需要进行裁定这个object的真正类型,并且保证不同object的vptr不会因为这个对象的创建/赋值而改变。故,派生类会被裁切出基类的部分,但是基类无法扩充成派生类对象(因为无法给出相应的vptr)。

 

 

程序内存

global object的内存保证会在每一次程序启动的时候清零;local object的内存保存在栈,heap object保存在自由空间(堆),它们的状态位上一次使用的状态。

 

 

 

chapter2

默认构造函数default constructor

如果object内含其他object,则default constructor会自动按照“类内声明这些object的顺序“进行default constructor的调用(执行这些功能的代码会插入在任何用户写在构造函数体内的代码前)。即使声明了含有参数的constructor或显示调用了一些构造函数(或使用部分初始化列表),编译器仍然会把没有调用的默认函数插入。这一条对继承的基类也同样奏效,调用基类的默认构造函数的代码也会插入到每个构造函数(按照继承的顺序,而不是初始化列表的顺序)

 

会合成default constructor构造函数的情况

1、拥有object成员,需要调用object的default constructor

2、拥有基类,而基类有合成/声明的default constructor

3、具有virtual function,需要创建vptr,所以基类拥有virtual function在非private时也是(在private的话是不需要去创建的,因为调用不了)

4、具有virtual base class,需要为每个virtual base class创建一个指向基类的指针,放在派生类的每个虚拟继承的基类之中,也就是每有一个虚拟基类就增加一个虚指针的空间。

其他时候,或许可以使用无参构造,但实际没有合成构造函数。也就是说并不是没有default构造,编译器就会合成一个。另一方面,默认构造并不会初始化全部的成员,只会初始化对象成员,内置类型不会拥有初始化值。

class A {
public:
	//A() :num(1) { cout << "A" << endl; }
	int num;
};

class B {
public:
	//B() :num(2) { cout << "B" << endl; }
	int num;
};

class C {
public:
	C(int n) :num(n) { cout << "C" << endl; }
	int num;
	B b;
	A a;
};

int main()
{
	C c(3);//c中的b,a里的num都没有初始化,但它们的默认构造函数是调用了的
	cout << c.a.num << endl;
	cout << c.b.num << endl;
	cout << c.num << endl;

	//A a;
	//cout << a.num << endl;//无法通过编译,因为没有初始化num,但是不用就不会有问题
}

 

 

2.2

拷贝构造函数copy constructor

通常object可以进行copy,但是并不代表其有一个拷贝构造函数,而是其使用了编译器的memberwise initialization -> 对每个变量进行bitwise copy(逐位复制),如果有对象成员,那么就对这个对象进行递归的memberwise initialization。这个时候其实没有一个实际合成的拷贝构造函数。

 

会合成拷贝构造函数的情况

1、对象成员有显示声明/合成的拷贝构造

2、基类具有显示声明/合成的拷贝构造

3、拥有vptr

4、拥有virtual base class

1与2需要合成拷贝构造来调用相应的构造,3与4需要拷贝构造来创建正确的vptr,这一点特别发生在把派生类对象赋值给基类对象进行初始化,编译器需要决定vptr的类别。

 

 

2.3

初始化参数的方法

1、先创建一个临时对象,调用拷贝构造复制传入的参数,然后把function改为接受一个引用,引用的就是这个临时对象的地址。(需要一组拷贝/析构)//通常做法

2、直接在参数的位置上构造这个对象(构造/析构)

 

返回值的初始化

1、返回对象的时候,先为函数增加一个引用参数作为返回结果,然后在return前调用copy constructor赋值一个对象,把这个对象地址给第二个参数。、

2、这个返回的对象如果没有用来初始化另一个对象,那么就会被析构(即使使用=赋值给另一个对象也不行,使用=只是进行了拷贝),用来初始化另一个对象的时候并不是调用拷贝构造函数,而是把返回的对象保留,变成需要创建的对象。

int num;
class A {
public:
	A() { cout << "default" << endl; num++; }
	A(const A &a) { cout << "copy " << a.name << endl; num++; }
	~A() { cout << name << "'s desstructor" << endl; }
	string name;
};

A test(A a) { 
	cout << "-------" << endl;
	A b = a;
	b.name = "b";
	cout << "-------" << endl;
	return b;
}

int main()
{
	A a;
	a.name = "a";
	A b(test(a));
	cout << num << endl;
	cout << b.name << endl;
}

A a;

a = func();//销毁

 

A a(func()); 或 A a = func();//保留,变成a

 

在使用者层面进行返回值以及参数传递的优化

引用代替对象作为参数,return 语句调用构造函数返回临时创建对象

X fun(const X&lhs,const X&rhs)

{ return X(lhs,rhs); }

//如此就没有任何由编译器创建的临时对象产生了,不过这样的return是可能出问题的

 

引用参数不会进行对象复制,显示调用构造也避免了对返回的对象进行复制。但是要慎用,比如smart_ptr就不可以这么返回,这样会导致智能指针在语句结束后被析构,也会导致其指向的对象被释放(可能被释放,如果你返回的这个对象没有被用来初始化,那么这个对象就要被销毁,销毁智能指针可能会销毁其管理的内置指针指向的值)。

 

 

在编译器层面进行优化

X fun()

{

    X xx;

    //进行一次对xx的拷贝,然后析构xx,再把拷贝所得的结果返回

    return xx;

}

优化为

void fun(X &result)

{

    result.X::X();

    return;

}

也就是把接受结果的对象的地址作为引用参数,然后直接调用构造函数,并且不返回值,避免了拷贝的进行。

也可以说是使用引用参数代替了return返回结果,成为name return value(NRV优化)

这个优化只有在copy constructor发生了inline的时候发生(可能你手动加上了inline都还没发生,实际上我就没试到发生过)

 

 

copy constructor的取舍(剔除)

这条并不是说什么时候不定义copy constructor,而是系统什么时候不选择copy constructor

初始化形式

A a(1024)//单一参数构造

A a = A(1024)//拷贝运算符

A a = (A)(1024)//强制类型转换

以上三种形式,后两者似乎应该是先调用默认构造创建a,然后再进行拷贝构造,但实际上,三者的效果完全一致,剔除了copy constructor的调用,使用的一致是单一参数构造函数。

 

copy costructor定义的取舍

对于一个内部只有非静态类型成员的对象,也许不要定义copy costructor会更好,即使自定义的copy constructor只是简单复制数值,它的效率也不会比编译器的bitwise copy更好,它的inline copy constructor出现的时候应该是能够确认拷贝操作大量出现的时候才定义,用来启动NRV优化,否则不定义就好了。

 

 

memset与memcopy

memcopy(pointer result,pointer locate,size)//复制pointer locate开始size大小的内存块

memset(pointer result,0/-1,size)//为指定内存块填入0/-1不能是其他值

这两个函数均无法在有虚指针的时候使用

因为他们会把vptr也一起复制/清零

 

 

初始化list

初始化的顺序是成员声明的顺序,而不是初始化列表中使用的顺序。

初始化代码会插入在任何用户写在constructor中的代码之前。

允许使用类内函数在初始化列表中初始化某个成员,也可以用来当做基类构造函数的参数,但是最好不要这么做,因为可能无法确定这个函数对自身成员的依赖性。

如果要用一个成员初始化另一个成员,那么考虑把这个操作放到函数体里,保证初始化代码已经发生

 

 

必须要使用初始化list的情况

1、const/reference成员初始化

2、需要调用基类除了默认构造函数以外的构造函数

3、需要调用成员除了默认构造函数以外的构造函数

资源下载链接为: https://pan.quark.cn/s/22ca96b7bd39 在当今的软件开发领域,自动化构建与发布是提升开发效率和项目质量的关键环节。Jenkins Pipeline作为种强大的自动化工具,能够有效助力Java项目的快速构建、测试及部署。本文将详细介绍如何利用Jenkins Pipeline实现Java项目的自动化构建与发布。 Jenkins Pipeline简介 Jenkins Pipeline是运行在Jenkins上的套工作流框架,它将原本分散在单个或多个节点上独立运行的任务串联起来,实现复杂流程的编排与可视化。它是Jenkins 2.X的核心特性之,推动了Jenkins从持续集成(CI)向持续交付(CD)及DevOps的转变。 创建Pipeline项目 要使用Jenkins Pipeline自动化构建发布Java项目,首先需要创建Pipeline项目。具体步骤如下: 登录Jenkins,点击“新建项”,选择“Pipeline”。 输入项目名称和描述,点击“确定”。 在Pipeline脚本中定义项目字典、发版脚本和预发布脚本。 编写Pipeline脚本 Pipeline脚本是Jenkins Pipeline的核心,用于定义自动化构建和发布的流程。以下是个简单的Pipeline脚本示例: 在上述脚本中,定义了四个阶段:Checkout、Build、Push package和Deploy/Rollback。每个阶段都可以根据实际需求进行配置和调整。 通过Jenkins Pipeline自动化构建发布Java项目,可以显著提升开发效率和项目质量。借助Pipeline,我们能够轻松实现自动化构建、测试和部署,从而提高项目的整体质量和可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值