对象成员初始化
初始化器列表
下面是一个示例类的构造函数,其中包含初始化器列表:
class Example {
public:
int x;
double y;
std::string z;
Example(int a, double b, std::string c) : x(a), y(b), z(c) {}
};
在这个示例中,构造函数使用了初始化器列表来分别初始化 x
、y
和 z
成员变量。可以看到,每个成员变量的初始值都在冒号后跟着相应的变量名和初始值。这样就可以方便地初始化多个成员变量,并提高代码可读性和效率。
默认成员初始化器
默认成员初始化器
class Example {
public:
int x = 0; // 默认成员初始化器
Example(int a) : x(a) {}
};
在这个示例中,x
成员变量被赋予了默认值 0
,因此即使没有在构造函数中显示地对其赋值,在对象创建时它仍将被初始化为 0
。如果在构造函数中提供了显式的初始值,则该值将覆盖默认初始化值。
必须使用初始化器列表:
常量成员,因为常量只能初始化不能赋值,所以必须放在初始化器列表里面
引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要使用初始化器
没有默认构造函数的类类型,因为使用初始化器列表可以不必调用无参构造函数来初始化,而是直接其他构造函数初始化
成员变量声明的顺序
成员是按照他们在类中声明的顺序
进行初始化的,而不是按照他们在初始化器列表出现的顺序初始化的:
class foo
{
public:
int i ;int j ;
foo(int x):j(x), i(j){}
// i值未定义
};
这里i的值是未定义的因为虽然j在初始化器列表里面出现在i前面,但是i先于j定义,所以先初始化i,而i由j初始化,此时j尚未初始化,所以导致i的值未定义。一个好的习惯是,按照成员声明的顺序进行初始化。
内存布局
全局数据区(data area) 放全局变量,静态数据和常量。
代码区(code area) 所有类成员函数和非成员函数代码
栈区(stack area) 为运行函数而分配的局部变量、函数参数、返回数据、返回地址等
堆区(heap area)(即自由存储区) 申请的空间
对象方法静态联编
指在编译阶段,就能直接使用代码段函数地址调用动态对象的方法。该方法仅需要向非静态成员函数传送this指针,即可用静态函数调用实现动态调用效果。
date1.Increment(…)
实际上为 DATE:: Increment(&date1, …)
拷贝构造函数
在定义语句中用同类型的对象初始化另一个对象。
//假定C为已定义的类,则
C obja; //调用C的(1)无参构造函数,如无(1)(2)构造函数则用默认构造函数
C obja(1,2) //调用C的有参(2)普通构造函数
/*调用C的(3)拷贝构造函数用对象obja初始化对象objb。如果有为C类明确定义拷贝构造函数,将调用这个拷贝构造函数;如果没有为C类定义拷贝构造函数,将调用默认拷贝构造函数。*/
C objb(obja);
C objb = obja ;
//两者等价(注意:不是赋值运算)
用类类型本身作形式参数。
该参数传递方式为按引用传递,避免在函数调用过程中生成形参副本。
该形参声明为const,以确保在拷贝构造函数中不修改实参的值
C::C(const C& obj);
例如:
COMPLEX(const
COMPLEX& other);
形参类型为该类类型本身且参数传递方式为按引用传递。
用一个已存在的该类对象初始化新创建的对象。
每个类都必须
有拷贝构造函数:
用户可根据自己的需要显式定义拷贝构造函数。
若用户未提供,则该类使用由系统提供的缺省拷贝构造函数(可用=default),也可用 =delete 弃置该函数。
缺省拷贝构造函数按照初始化顺序,对对象的各基类和非静态成员进行完整的逐成员复制,完成新对象的初始化。即逐一调用成员的拷贝构造,如果成员是基础类型,则复制值(赋值)。
隐式调用复制构造(1):对象作为函数值参
void
fun(C temp)
{ …
}
fun(obja);//obja传递给fun函数,创建形参对象temp时,调用C的拷贝构造函数用对象obja初始化对象temp,temp生存期结束被撤销时,调用析构函数
隐式调用复制构造(2):对象作为值从函数返回
生成一个临时对象作为函数的返回结果:当函数返回一对象时,系统将自动创建一个临时对象来保存函数的返回值。创建此临时对象时调用拷贝构造函数,当函数调用表达式结束后,撤销该临时对象时,调用析构函数。
C fun2()
{
C t;
return t ;
}
b = fun2( );
t --> 临时对象 -->
b
但现在的gcc/g++不这么处理,会做一个优化。在C函数里有个t变量,离开func时不撤销这个对象,而是让new C和这个对象关联起来。也就是说t的地址和new C是一样的。
复制策略:拷贝构造函数自定义
- 对于不含指针成员的类,使用系统提供(编译器合成)的默认拷贝构造函数即可。
- 缺省拷贝构造函数使用浅复制策略,不能满足对含指针数据成员的类需要。
- 含指针成员的类通常应重写以下内容:
•构造函数(及拷贝构造函数)中分配内存,深复制策略
•= 操作重写,完成对象深复制策略
•析构函数中释放内存
(浅拷贝只复制成员指针的值,而不复制指向的对象实体,导致新旧对象成员指针指向同一块内存。但深拷贝要求成员指针指向的对象也要复制,新对象跟原对象的成员指针不会指向同一块内存,修改新对象不会改到原对象。)