Skip to content

Ch 20: 构造函数和析构函数

  • 掌握各种构造函数的写法和适用场景
  • 深入理解析构函数的作用和 RAII 模式
  • 学会正确使用初始化列表
  • 理解委托构造和移动构造
  • 掌握 Rule of 5 和 Rule of 0
  • 理解 default 和 delete 的用法
class Resource:
def __init__(self, name):
self.name = name
print(f"Creating {name}")
def __del__(self):
# Python 垃圾回收,不保证及时调用
print(f"Destroying {self.name}")
def process(self):
print(f"Processing {self.name}")
# Python 的初始化
r = Resource("File") # Creating File
r.process()
del r # 显式删除会调用 __del__
#include <string>
#include <iostream>
class Resource {
public:
Resource(const std::string& name) : name_(name) {
std::cout << "Creating " << name_ << "\n";
}
// 析构函数 - 对象销毁时自动调用
~Resource() {
std::cout << "Destroying " << name_ << "\n";
}
void process() {
std::cout << "Processing " << name_ << "\n";
}
private:
std::string name_;
};
int main() {
Resource r("File"); // Creating File
r.process(); // Processing File
// 析构函数在 r 离开作用域时自动调用
return 0;
}
特性PythonC++
初始化__init__构造函数
清理__del__(不确定)析构函数(确定)
生命周期垃圾回收确定性(RAII)
调用时机不确定离开作用域时
#include <iostream>
#include <string>
class Widget {
public:
// 方式 1:用户声明的默认构造函数
Widget() : value_(0) {
std::cout << "Widget()\n";
}
// 方式 2:使用 = default(C++11,推荐)
Widget() = default;
int value() const { return value_; }
private:
int value_;
};
int main() {
Widget w1; // 调用默认构造函数
Widget w2{}; // 同样
Widget w3 = Widget(); // 同样
std::cout << w1.value() << "\n";
}
#include <string>
#include <iostream>
class Person {
public:
// 单参数构造(可以用 = 直接初始化)
explicit Person(const std::string& name) : name_(name), age_(0) {}
// 多参数构造
Person(const std::string& name, int age)
: name_(name), age_(age) {}
// 带有默认参数的构造函数
Person(const std::string& name = "Unknown", int age = 0)
: name_(name), age_(age) {}
std::string info() const {
return name_ + ", " + std::to_string(age_);
}
private:
std::string name_;
int age_;
};
int main() {
Person p1; // 使用默认值
Person p2("Alice"); // Alice, 0
Person p3("Bob", 30); // Bob, 30
Person p4{"Charlie", 25}; // Charlie, 25
std::cout << p1.info() << "\n";
std::cout << p2.info() << "\n";
std::cout << p3.info() << "\n";
}
#include <string>
#include <iostream>
class Rectangle {
private:
double width_;
double height_;
public:
// 委托到四参数构造函数
Rectangle() : Rectangle(0, 0) {}
// 委托到四参数构造函数
Rectangle(double size) : Rectangle(size, size) {}
// 主构造函数
Rectangle(double w, double h) : width_(w), height_(h) {}
double area() const { return width_ * height_; }
};
class Logger {
private:
std::string name_;
int level_;
public:
// 委托到另一个构造函数
Logger() : Logger("default", 0) {}
Logger(const std::string& name) : Logger(name, 0) {}
Logger(const std::string& name, int level)
: name_(name), level_(level) {}
void log(const std::string& msg) const {
std::cout << "[" << level_ << "] " << name_ << ": " << msg << "\n";
}
};
int main() {
Rectangle r1; // 0 x 0
Rectangle r2(5); // 5 x 5
Rectangle r3(3, 4); // 3 x 4
std::cout << "Areas: " << r1.area() << ", " << r2.area() << ", " << r3.area() << "\n";
Logger l1;
Logger l2("App");
Logger l3("App", 3);
l1.log("Started");
l2.log("Running");
l3.log("Warning");
}
#include <string>
#include <iostream>
class Person {
public:
Person(const std::string& name, int age)
: name_(name), age_(age) {}
// 拷贝构造函数 - 用另一个对象初始化
Person(const Person& other)
: name_(other.name_), age_(other.age_) {
std::cout << "Copying " << name_ << "\n";
}
std::string info() const {
return name_ + ", " + std::to_string(age_);
}
private:
std::string name_;
int age_;
};
int main() {
Person p1("Alice", 30);
// 调用拷贝构造函数
Person p2(p1); // Copying Alice
Person p3 = p1; // Copying Alice
// 函数参数传递
void process(Person p);
process(p1); // 拷贝 Alice
// 函数返回(可能触发拷贝)
Person create_person() {
Person temp("Bob", 25);
return temp; // 可能触发拷贝或移动
}
return 0;
}
#include <string>
#include <iostream>
class Person {
private:
std::string name_;
int* data_;
public:
Person(const std::string& name, int size)
: name_(name), data_(new int[size]) {
std::cout << "Constructing " << name_ << "\n";
}
// 拷贝构造函数
Person(const Person& other)
: name_(other.name_), data_(new int[100]) {
std::cout << "Copying " << name_ << "\n";
// 深拷贝
std::copy(other.data_, other.data_ + 100, data_);
}
// 移动构造函数 - 转移资源所有权
Person(Person&& other) noexcept
: name_(std::move(other.name_)), data_(other.data_) {
std::cout << "Moving " << name_ << "\n";
other.data_ = nullptr; // 防止析构函数释放
}
// 移动赋值运算符
Person& operator=(Person&& other) noexcept {
if (this != &other) {
delete[] data_;
name_ = std::move(other.name_);
data_ = other.data_;
other.data_ = nullptr;
}
std::cout << "Move assigning " << name_ << "\n";
return *this;
}
~Person() {
std::cout << "Destroying " << name_ << "\n";
delete[] data_;
}
};
int main() {
Person p1("Alice", 100);
Person p2(std::move(p1)); // 移动,p1 变为空壳
std::cout << "p2's name: " << p2.info() << "\n";
// p1 现在处于有效但未定义状态,data_ 为 nullptr
return 0;
}
#include <string>
#include <iostream>
class Person {
public:
// ✅ 推荐:初始化列表
Person(const std::string& name, int age)
: name_(name), age_(age) {} // 直接初始化
// ❌ 不推荐:赋值方式
Person(const std::string& name, int age) {
name_ = name; // 先默认构造,再赋值
age_ = age; // 多余开销
}
private:
std::string name_; // 默认构造
int age_; // 默认构造
};
#include <iostream>
class Order {
public:
Order(int priority) : id_(next_id_++), priority_(priority) {
std::cout << "Order " << id_ << " created\n";
}
private:
int id_;
int priority_;
static int next_id_;
};
int Order::next_id_ = 1;
int main() {
Order o1(1); // id = 1
Order o2(2); // id = 2
Order o3(3); // id = 3
return 0;
}
#include <string>
class Widget {
private:
// 方式 1:声明时默认成员初始化(C++11)
int value_ = 0;
std::string name_ = "default";
// 方式 2:使用 default
double factor_ = 1.0;
// 方式 3:使用花括号
bool enabled_{true};
public:
// 构造函数中的初始化列表会覆盖默认值
Widget() = default;
};
#include <fstream>
#include <string>
#include <iostream>
class FileHandler {
public:
explicit FileHandler(const std::string& filename)
: filename_(filename) {
file_.open(filename, std::ios::out);
if (file_.is_open()) {
std::cout << "File opened: " << filename_ << "\n";
}
}
// 析构函数 - 对象销毁时自动调用
~FileHandler() {
if (file_.is_open()) {
file_.close();
std::cout << "File closed: " << filename_ << "\n";
}
}
void write(const std::string& data) {
if (file_.is_open()) {
file_ << data;
}
}
private:
std::fstream file_;
std::string filename_;
};
int main() {
{
FileHandler fh("test.txt");
fh.write("Hello, World!");
// fh 离开作用域,析构函数被调用
}
std::cout << "FileHandler destroyed\n";
return 0;
}
#include <mutex>
#include <iostream>
// RAII 模式确保资源在离开作用域时被释放
class Lock {
private:
std::mutex& mutex_;
bool locked_ = false;
public:
explicit Lock(std::mutex& m) : mutex_(m) {
mutex_.lock();
locked_ = true;
std::cout << "Locked\n";
}
~Lock() {
if (locked_) {
mutex_.unlock();
std::cout << "Unlocked\n";
}
}
};
std::mutex shared_mutex;
void process() {
Lock lock(shared_mutex); // 获取锁
// ... 执行工作 ...
} // 离开作用域,自动释放锁
int main() {
std::cout << "Starting\n";
process();
std::cout << "Done\n";
return 0;
}
#include <iostream>
#include <stdexcept>
class Cleanup {
public:
~Cleanup() {
std::cout << "Cleanup performed\n";
}
};
void risky_function(bool should_throw) {
Cleanup cleanup; // 确保清理
std::cout << "Doing work\n";
if (should_throw) {
throw std::runtime_error("An error occurred!");
}
std::cout << "Work completed\n";
}
int main() {
try {
risky_function(false); // 不抛异常
} catch (...) {
std::cout << "Exception caught\n";
}
try {
risky_function(true); // 抛异常
} catch (...) {
std::cout << "Exception caught\n";
}
std::cout << "Done\n";
return 0;
}

C++ 类有 5 个特殊成员函数:

  1. 析构函数 ~Class()
  2. 拷贝构造函数 Class(const Class&)
  3. 拷贝赋值运算符 Class& operator=(const Class&)
  4. 移动构造函数 Class(Class&&)
  5. 移动赋值运算符 Class& operator=(Class&&)

如果需要自定义任意一个,通常需要全部定义:

#include <string>
#include <iostream>
#include <utility>
class RuleOf5 {
private:
std::string data_;
int* buffer_;
public:
explicit RuleOf5(const std::string& d, int size)
: data_(d), buffer_(new int[size]) {
std::cout << "Constructing RuleOf5\n";
}
// 析构函数
~RuleOf5() {
std::cout << "Destructing RuleOf5\n";
delete[] buffer_;
}
// 拷贝构造函数
RuleOf5(const RuleOf5& other)
: data_(other.data_), buffer_(new int[100]) {
std::cout << "Copying RuleOf5\n";
std::copy(other.buffer_, other.buffer_ + 100, buffer_);
}
// 拷贝赋值运算符
RuleOf5& operator=(const RuleOf5& other) {
if (this != &other) {
std::cout << "Copy assigning RuleOf5\n";
data_ = other.data_;
delete[] buffer_;
buffer_ = new int[100];
std::copy(other.buffer_, other.buffer_ + 100, buffer_);
}
return *this;
}
// 移动构造函数
RuleOf5(RuleOf5&& other) noexcept
: data_(std::move(other.data_)), buffer_(other.buffer_) {
std::cout << "Moving RuleOf5\n";
other.buffer_ = nullptr; // 防止双重删除
}
// 移动赋值运算符
RuleOf5& operator=(RuleOf5&& other) noexcept {
if (this != &other) {
std::cout << "Move assigning RuleOf5\n";
delete[] buffer_;
data_ = std::move(other.data_);
buffer_ = other.buffer_;
other.buffer_ = nullptr;
}
return *this;
}
};
int main() {
RuleOf5 a("Hello", 100);
std::cout << "--- Copy ---\n";
RuleOf5 b(a); // 拷贝构造
std::cout << "--- Move ---\n";
RuleOf5 c(std::move(a)); // 移动构造
std::cout << "--- Copy assign ---\n";
b = c; // 拷贝赋值
std::cout << "--- Move assign ---\n";
b = std::move(c); // 移动赋值
std::cout << "--- End ---\n";
return 0;
}

如果类不需要管理资源(使用 RAII 包装器),让编译器生成默认版本:

#include <string>
#include <iostream>
// Rule of 0 - 不需要定义任何特殊成员函数
class Simple {
public:
Simple(int v) : value_(v) {}
int value() const { return value_; }
private:
int value_;
};
// 编译器自动生成所有 5 个特殊成员函数
// 所有都是 default 的行为
// 另一个 Rule of 0 的例子
class Point {
public:
Point(double x, double y) : x_(x), y_(y) {}
double x() const { return x_; }
double y() const { return y_; }
private:
double x_;
double y_;
};
// std::string 本身已经正确处理资源
class Person {
public:
Person(const std::string& name) : name_(name) {}
const std::string& name() const { return name_; }
private:
std::string name_;
};
// 不需要自定义拷贝/移动,std::string 会正确处理
int main() {
Simple s1(1);
Simple s2(s1); // 默认拷贝
Person p1("Alice");
Person p2(p1); // 默认拷贝
std::cout << "Rule of 0 works\n";
return 0;
}
#include <string>
#include <iostream>
class Widget {
public:
// 要求编译器生成默认版本
Widget() = default;
// 显式默认的拷贝构造
Widget(const Widget&) = default;
// 显式默认的移动构造
Widget(Widget&&) = default;
// 显式默认的析构函数(virtual)
virtual ~Widget() = default;
// 显式默认的拷贝赋值
Widget& operator=(const Widget&) = default;
// 显式默认的移动赋值
Widget& operator=(Widget&&) = default;
};
struct Base {
virtual ~Base() = default;
};
struct Derived : Base {
~Derived() override = default;
};
#include <string>
#include <iostream>
// 禁止拷贝的类
class NonCopyable {
public:
NonCopyable() = default;
// 显式删除拷贝构造函数
NonCopyable(const NonCopyable&) = delete;
// 显式删除拷贝赋值运算符
NonCopyable& operator=(const NonCopyable&) = delete;
// 允许移动
NonCopyable(NonCopyable&&) = default;
NonCopyable& operator=(NonCopyable&&) = default;
};
int main() {
NonCopyable nc1;
// NonCopyable nc2 = nc1; // ❌ 编译错误!拷贝被禁用
NonCopyable nc3 = std::move(nc1); // OK,移动允许
return 0;
}
#include <iostream>
// 禁止某些重载
class Integer {
public:
Integer(int value) : value_(value) {}
// 禁止从 double 构造(避免精度丢失)
explicit Integer(double) = delete;
// 禁止从 bool 构造(避免意外)
explicit Integer(bool) = delete;
int value() const { return value_; }
private:
int value_;
};
// 只允许某些值
class PositiveInt {
public:
explicit PositiveInt(int value) : value_(value) {}
// 禁止负数
static PositiveInt create(int value) {
if (value < 0) {
throw std::invalid_argument("Negative not allowed");
}
return PositiveInt(value);
}
int value() const { return value_; }
private:
int value_;
};
// 禁止某些函数调用
void process(int* ptr) {
// 处理指针
}
// 禁止传递 null 指针的重载
void process(std::nullptr_t) = delete;
int main() {
Integer i1(42); // OK
// Integer i2(3.14); // ❌ 编译错误!double 构造函数被删除
// Integer i3(true); // ❌ 编译错误!bool 构造函数被删除
// process(nullptr); // ❌ 编译错误!
std::cout << i1.value() << "\n";
return 0;
}
#include <iostream>
#include <stdexcept>
// 简化版 unique_ptr
template<typename T>
class UniquePtr {
private:
T* ptr_ = nullptr;
public:
// 默认构造
UniquePtr() = default;
// 构造函数,接受原始指针
explicit UniquePtr(T* raw_ptr) : ptr_(raw_ptr) {}
// 禁用拷贝
UniquePtr(const UniquePtr&) = delete;
UniquePtr& operator=(const UniquePtr&) = delete;
// 移动构造函数
UniquePtr(UniquePtr&& other) noexcept : ptr_(other.ptr_) {
other.ptr_ = nullptr;
}
// 移动赋值
UniquePtr& operator=(UniquePtr&& other) noexcept {
if (this != &other) {
delete ptr_;
ptr_ = other.ptr_;
other.ptr_ = nullptr;
}
return *this;
}
// 解引用
T& operator*() const {
if (!ptr_) throw std::runtime_error("Dereferencing null pointer");
return *ptr_;
}
// 箭头运算符
T* operator->() const {
if (!ptr_) throw std::runtime_error("Accessing null pointer");
return ptr_;
}
// 获取原始指针
T* get() const { return ptr_; }
// 释放所有权
T* release() {
T* temp = ptr_;
ptr_ = nullptr;
return temp;
}
// 析构函数
~UniquePtr() {
delete ptr_;
}
};
struct Resource {
std::string name;
Resource(const std::string& n) : name(n) {
std::cout << "Acquiring " << name << "\n";
}
~Resource() {
std::cout << "Releasing " << name << "\n";
}
};
int main() {
{
UniquePtr<Resource> r1(new Resource("File"));
std::cout << "r1 owns: " << r1->name << "\n";
// 移动
UniquePtr<Resource> r2 = std::move(r1);
// std::cout << r1->name << "\n"; // ❌ r1 现在是空
std::cout << "r2 owns: " << r2->name << "\n";
} // Resource 在这里被释放
std::cout << "Done\n";
return 0;
}
特殊成员函数作用默认行为
析构函数 ~Class()对象销毁时清理默认析构非virtual
拷贝构造 Class(const Class&)用同类对象初始化浅拷贝
拷贝赋值 operator=(const Class&)赋值操作浅拷贝
移动构造 Class(Class&&)转移所有权转移资源
移动赋值 operator=(Class&&)转移所有权转移资源

选择原则:

  • 管理资源(指针、文件句柄等)→ 自定义全部 5 个(Rule of 5)
  • 不管理资源 → 使用默认(Rule of 0)
  • = default 显式要求默认实现
  • = delete 禁用不需要的函数

下章预告:ch21 学习继承和多态,理解基类、派生类和虚函数。