C++ 类继承中的构造函数、拷贝控制成员与析构函数行为详解
1. 构造函数调用规则
1.1 默认构造函数
- 隐式调用:若派生类未显式定义构造函数,合成的默认构造函数会隐式调用 基类的默认构造函数。
- 错误示例:基类没有默认构造函数时,编译失败。
class Base {
public:
Base(int x) {} // 无默认构造函数
};
class Derived : public Base {
// 隐式生成 Derived(),尝试调用 Base(),但 Base 无默认构造函数,编译错误!
};
1.2 其他构造函数
- 显式调用基类构造函数:若派生类构造函数未显式调用基类构造函数,编译器会尝试调用基类的 默认构造函数。
class Base {
public:
Base() { std::cout << "Base()\n"; }
Base(int x) { std::cout << "Base(int)\n"; }
};
class Derived : public Base {
public:
Derived(int x) {
// 隐式调用 Base(),输出 "Base()"
std::cout << "Derived(int)\n";
}
Derived(int x, int y) : Base(x) {
// 显式调用 Base(int),输出 "Base(int)"
std::cout << "Derived(int, int)\n";
}
};
int main() {
Derived d1(10); // 输出 Base() → Derived(int)
Derived d2(10, 20); // 输出 Base(int) → Derived(int, int)
}
2. 拷贝控制成员
2.1 拷贝构造函数
- 隐式调用基类拷贝构造函数:派生类合成的拷贝构造函数会调用基类的拷贝构造函数。
class Base {
public:
Base() = default;
Base(const Base&) { std::cout << "Base copy\n"; }
};
class Derived : public Base {
// 隐式生成 Derived(const Derived&),调用 Base(const Base&)
};
int main() {
Derived d1;
Derived d2(d1); // 输出 "Base copy"
}
2.2 赋值运算符
- 隐式调用基类赋值运算符:派生类合成的赋值运算符会调用基类的赋值运算符。
class Base {
public:
Base& operator=(const Base&) {
std::cout << "Base assignment\n";
return *this;
}
};
class Derived : public Base {
// 隐式生成 operator=,调用 Base::operator=
};
int main() {
Derived d1, d2;
d1 = d2; // 输出 "Base assignment"
}
2.3 显式定义时的注意事项
- 必须显式调用基类成员:若显式定义拷贝构造函数或赋值运算符,需手动调用基类对应函数。
class Base { /* ... */ };
class Derived : public Base {
public:
Derived(const Derived& rhs) : Base(rhs) { // 显式调用基类拷贝构造函数
// 拷贝派生类成员
}
Derived& operator=(const Derived& rhs) {
Base::operator=(rhs); // 显式调用基类赋值运算符
// 赋值派生类成员
return *this;
}
};
3. 析构函数
3.1 隐式调用基类析构函数
- 销毁顺序:派生类析构函数执行完毕后,自动调用基类析构函数。
class Base {
public:
~Base() { std::cout << "~Base()\n"; }
};
class Derived : public Base {
public:
~Derived() { std::cout << "~Derived()\n"; }
};
int main() {
Derived d;
} // 输出 ~Derived() → ~Base()
3.2 基类析构函数必须为虚函数
- 关键规则:若通过基类指针删除派生类对象,基类析构函数必须为虚函数,否则导致 资源泄漏。
class Base {
public:
virtual ~Base() { std::cout << "~Base()\n"; } // 虚析构函数
};
class Derived : public Base {
public:
~Derived() { std::cout << "~Derived()\n"; }
};
int main() {
Base* ptr = new Derived();
delete ptr; // 正确调用 ~Derived() → ~Base()
}
4. 构造与析构顺序
4.1 构造顺序
- 基类 → 成员对象 → 派生类
class Member {
public:
Member() { std::cout << "Member()\n"; }
};
class Base {
public:
Base() { std::cout << "Base()\n"; }
};
class Derived : public Base {
Member m;
public:
Derived() { std::cout << "Derived()\n"; }
};
int main() {
Derived d;
} // 输出 Base() → Member() → Derived()
4.2 析构顺序
- 派生类 → 成员对象 → 基类
// 使用上述类定义
int main() {
Derived d;
} // 输出 Derived() → Member() → Base()
↓ 析构时 ↓
~Derived() → ~Member() → ~Base()
5. 显式定义特殊成员函数
5.1 必须显式调用基类成员的情况
- 自定义拷贝构造函数:
class Base { /* ... */ };
class Derived : public Base {
public:
Derived(const Derived& rhs)
: Base(rhs) { // 必须显式调用基类拷贝构造函数
// 拷贝派生类成员
}
};
- 自定义赋值运算符:
class Derived : public Base {
public:
Derived& operator=(const Derived& rhs) {
Base::operator=(rhs); // 必须显式调用基类赋值运算符
// 赋值派生类成员
return *this;
}
};
- 自定义移动操作(C++11):
class Derived : public Base {
public:
Derived(Derived&& rhs)
: Base(std::move(rhs)) { // 显式移动基类部分
// 移动派生类成员
}
};
总结
- 构造顺序:基类 → 成员对象 → 派生类。
- 析构顺序:派生类 → 成员对象 → 基类。
- 隐式调用规则:
- 默认构造函数调用基类默认构造函数。
- 拷贝构造函数调用基类拷贝构造函数。
- 赋值运算符调用基类赋值运算符。
- 显式定义时的要求:需手动调用基类对应函数。
- 基类析构函数必须为虚函数,避免资源泄漏。
示例代码整合:
#include <iostream>
class Base {
public:
Base() { std::cout << "Base()\n"; }
Base(const Base&) { std::cout << "Base copy\n"; }
virtual ~Base() { std::cout << "~Base()\n"; }
Base& operator=(const Base&) {
std::cout << "Base assignment\n";
return *this;
}
};
class Member {
public:
Member() { std::cout << "Member()\n"; }
~Member() { std::cout << "~Member()\n"; }
};
class Derived : public Base {
Member m;
public:
Derived() { std::cout << "Derived()\n"; }
Derived(const Derived& rhs) : Base(rhs), m(rhs.m) {
std::cout << "Derived copy\n";
}
Derived& operator=(const Derived& rhs) {
Base::operator=(rhs);
m = rhs.m;
std::cout << "Derived assignment\n";
return *this;
}
~Derived() { std::cout << "~Derived()\n"; }
};
int main() {
Derived d1; // 构造顺序:Base → Member → Derived
Derived d2(d1); // 拷贝构造:Base copy → Member copy → Derived copy
d1 = d2; // 赋值操作:Base assignment → Derived assignment
}
// 析构顺序:~Derived → ~Member → ~Base(两次)
输出:
Base()
Member()
Derived()
Base copy
Member copy
Derived copy
Base assignment
Derived assignment
~Derived()
~Member()
~Base()
~Derived()
~Member()
~Base()
690

被折叠的 条评论
为什么被折叠?



