对象成员初始化

初始化器列表

下面是一个示例类的构造函数,其中包含初始化器列表:

class Example {
public:
    int x;
    double y;
    std::string z;

    Example(int a, double b, std::string c) : x(a), y(b), z(c) {}
};

在这个示例中,构造函数使用了初始化器列表来分别初始化 xyz 成员变量。可以看到,每个成员变量的初始值都在冒号后跟着相应的变量名和初始值。这样就可以方便地初始化多个成员变量,并提高代码可读性和效率。

默认成员初始化器

默认成员初始化器

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是一样的。

复制策略:拷贝构造函数自定义

  1. 对于不含指针成员的类,使用系统提供(编译器合成)的默认拷贝构造函数即可。
  2. 缺省拷贝构造函数使用浅复制策略,不能满足对含指针数据成员的类需要。
  3. 含指针成员的类通常应重写以下内容:

•构造函数(及拷贝构造函数)中分配内存,深复制策略

•= 操作重写,完成对象深复制策略

•析构函数中释放内存
(浅拷贝只复制成员指针的值,而不复制指向的对象实体,导致新旧对象成员指针指向同一块内存。但深拷贝要求成员指针指向的对象也要复制,新对象跟原对象的成员指针不会指向同一块内存,修改新对象不会改到原对象。)

Last modification:March 30, 2023
如果觉得我的文章对你有用,请随意赞赏