共计 1820 个字符,预计需要花费 5 分钟才能阅读完成。
对象创建时的构造函数调用
构造函数是 C ++ 中类对象的初始化入口,它的调用时机直接影响对象的行为和内存状态。不同创建方式下,构造函数的触发机制各有特点。

-
栈对象 :当在函数内部定义局部对象时,编译器会自动在代码插入点调用构造函数
void func() {MyClass obj; // 此处隐式调用 MyClass::MyClass() } -
堆对象 :通过 new 运算符创建的对象会先分配内存,然后立即调用构造函数
MyClass* pObj = new MyClass(); // 1. 分配内存 2. 调用构造函数 -
临时对象 :函数返回值或类型转换时产生的临时对象,其生命周期通常到表达式结束
MyClass createObj() {return MyClass(); // 调用构造函数创建临时对象 }
继承体系中的构造顺序
在继承关系中,构造函数的调用遵循从基类到派生类的顺序,这对多态的实现至关重要:
- 基类构造函数最先被调用
- 成员对象构造函数按声明顺序调用
- 最后执行派生类构造函数体
class Base {
public:
Base() { cout << "Base constructor" << endl;}
};
class Derived : public Base {
Member m;
public:
Derived() : m(), Base() {// 实际执行顺序:Base() -> m() -> Derived()
cout << "Derived constructor" << endl;
}
};
典型代码示例分析
示例 1:包含成员对象的类
class Engine {
public:
Engine() { cout << "Engine built" << endl;}
};
class Car {
Engine e; // 成员对象
public:
Car() { cout << "Car assembled" << endl;}
};
// 输出顺序:// Engine built
// Car assembled
示例 2:继承体系演示
class Animal {
public:
Animal() { cout << "Animal born" << endl;}
};
class Dog : public Animal {
public:
Dog() { cout << "Dog bark" << endl;}
};
// 创建 Dog 对象时输出:// Animal born
// Dog bark
示例 3:移动构造优化
class Buffer {
char* data;
public:
Buffer(size_t size) : data(new char[size]) {}
// 移动构造函数
Buffer(Buffer&& other) noexcept
: data(other.data) {other.data = nullptr;}
};
Buffer createBuffer() {Buffer buf(1024);
return buf; // 触发移动构造而非拷贝
}
避坑指南
异常处理
构造函数中抛出异常时:
– 已构造的成员和基类会被自动析构
– 对象本身不会调用析构函数
– 使用 function-try-block 捕获异常:
class SafeClass {
public:
SafeClass() try : resource(new int) {// 可能抛出异常的操作} catch(...) {
delete resource;
throw;
}
private:
int* resource;
};
避免对象切片
当派生类对象被按值传递给基类参数时,会发生对象切片:
void process(Animal a) {...}
Dog d;
process(d); // 只复制 Animal 部分,Dog 特性丢失
解决方案:
– 使用指针或引用传递
– 将基类设为抽象类
初始化列表的优势
与构造函数体内赋值相比,初始化列表:
1. 对 const 成员和引用成员必须使用
2. 避免先默认构造再赋值的开销
3. 保持与成员声明顺序一致
// 更高效的写法
class Widget {
std::vector<int> data;
public:
Widget(int count) : data(count) {} // 直接初始化};
思考与实践
- 设计一个禁止拷贝但允许移动的类需要哪些特殊成员函数的配合?
- 如果在基类构造函数中调用虚函数,实际执行的是基类还是派生类的实现?为什么?
- 如何通过构造函数设计实现只能通过工厂方法创建的对象?
理解构造函数的调用时机是掌握 C ++ 对象生命周期的关键。通过合理控制构造过程,可以优化性能、避免资源泄漏,并构建更健壮的对象体系。建议读者在实际项目中多观察构造 / 析构的调用日志,加深对对象生命周期的直观认识。
正文完
