C++创建对象过程
参考:
c++创建对象过程详解 - LEO__Y - 博客园 (cnblogs.com)
创建对象的过程
1.分配内存空间
2.初始化成员变量
3.调用构造方法
内存分配(Allocation)
全局对象和静态对象
编译器会为他们划分一个独立的段(全局段)为他们分配足够的空间
栈对象
MyClass obj; // 栈上分配
编译器在栈帧中直接预留空间,大小由
sizeof(MyClass)
决定。分配速度极快(只需移动栈指针),无需手动释放。
堆对象
MyClass* obj = new MyClass(); // 堆上分配
调用
operator new
在堆上分配内存(底层可能调用malloc
)。需要手动
delete
释放,否则内存泄漏。注意:对象在栈或者堆上整体分配的,那么类里面的成员就在哪个位置存着,对象是局部的,那么成员就是局部的,对象是全局的,那么成员就是全局的。当然,不包括静态成员,因为静态成员不属于对象,而是属于类的,所以会在创建对象之前,程序启动时就初始化,后续详细说明。
在看到C++的类成员被调用时,比如一个类的成员变量被调用,我总是会下意识地去想这个到底是局部变量还是全局变量,这是C语言的惯性思维,而在C++中,成员是随着对象的位置而存在的,对象在哪片内存上,成员就在哪片内存上,访问成员,都需要通过对象来调用,当然,除了静态对象。
记住,类本质上只是一种数据和函数的组织形式。
其实类就跟结构体一样,是个声明而已,只有创建对应实例的时候才能使用(除了静态成员)
初始化成员变量
成员变量的初始化发生在构造函数体执行之前,具体分为两个阶段:
- 在进入构造函数体之前,所有成员变量会先进行默认初始化(类类型调用默认构造函数)。
- 如果使用了成员初始化列表(member initializer list),则会用指定的值覆盖默认初始化的结果。
初始化 (Initialization)和赋值(Assignment)。初始化早于赋值,它是随着对象的诞生一起进行的。而赋值是在对象诞生以后又给予它一个新的值。对象经过初始化以后,我们仍然可以对其进行赋值。我们可以通过构造函数的实现体(即构造函数中由"{}"包裹的部分)来实现。
注意:如果使用了构造函数的初始化列表,则会在初始化时直接就赋予对应的值,而不必分成初始化和赋值两个步骤了。
默认初始化的规则
内置类型(如
int
、double
、指针等):如果未显式初始化,值是未定义的(除非是全局变量或
static
成员,会初始化为0)。类类型:
调用默认构造函数初始化。如果没有默认构造函数且未在初始化列表中显式初始化,则编译错误。
初始化方式
(1) 成员初始化列表(推荐)
class Example { public:Example(int x, int y) : a(x), b(y) { // 构造函数体(此时a和b已初始化)} private:int a;int b; };
时机:在构造函数体执行之前完成初始化。
特点:
直接调用成员的构造函数或赋值,效率更高(避免默认初始化 + 赋值的双重操作)。
对
const
成员、引用成员、没有默认构造函数的类成员必须用初始化列表。当类的成员变量是const 或者 引用类型时,必须使用初始化列表进行初始化,原因是:
const数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的。因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。所以不能在类的声明中初始化const数据成员,因为类的对象没被创建时,编译器不知道const数据成员的值是什么。
const数据成员的初始化只能在类的构造函数的初始化列表中进行。要想建立在整个类中都恒定的常量,应该用类中的枚举常量来实现,或者static cosnt。
引用的指向只能初始化,不能修改其指向,赋值过程是在修改其指向,其实这就是赋值和初始化的本质区别
(2) 构造函数体内赋值
class Example { public:Example(int x, int y) {a = x; // 赋值,非初始化b = y; // 赋值,非初始化} private:int a;int b; };
时机:在构造函数体内赋值时,成员变量已经经历了默认初始化(内置类型如
int
是未定义值,类类型调用默认构造函数)。缺点:效率较低(尤其是类类型成员,会先默认构造再赋值)。
特殊情况
静态成员变量(
static
):必须在类外单独定义和初始化(不占用对象内存)。
class Example {static int count; }; int Example::count = 0; // 必须在类外初始化
常量静态成员(
const static
):class Example {const static int MAX = 100; };
C++11 后的类内初始化
C++11允许在类定义中直接为成员变量赋默认值:
class Example {int a = 10; // 类内初始化std::string s = "Hello"; public:Example() {} // a和s会以10和"Hello"初始化 };
初始化时机:如果构造函数未在初始化列表中显式指定值,则使用类内初始值(本质上等同于编译器将类内初始化放到初始化列表中)。
为什么基本类型成员默认不初始化为0?
性能考虑:C++的设计哲学是“不为不需要的操作付出代价”。强制初始化为零可能影响性能(尤其是频繁创建对象的场景)。
明确性:要求程序员显式初始化,避免隐藏的错误(如误用未初始化的值)。
通过显式初始化成员变量,可以确保代码的可预测性和安全性。
更多待补充。