在这里插入图片描述

构造函数(Constructor)

什么是构造函数?

构造函数是一种特殊的成员函数,在创建类对象时自动调用,用于初始化对象的数据成员。

构造函数的特点

  1. 与类同名
  2. 没有返回类型
  3. 可以重载
  4. 自动调用

构造函数的类型

1. 默认构造函数
class MyClass {
private:
    int value;
    std::string name;
    
public:
    // 默认构造函数(无参数)
    MyClass() {
        value = 0;
        name = "Unknown";
        std::cout << "Default constructor called" << std::endl;
    }
};

// 使用
MyClass obj;  // 调用默认构造函数
2. 参数化构造函数
class MyClass {
private:
    int value;
    std::string name;
    
public:
    // 参数化构造函数
    MyClass(int val, const std::string& n) {
        value = val;
        name = n;
        std::cout << "Parameterized constructor called" << std::endl;
    }
};

// 使用
MyClass obj1(42, "Hello");
MyClass obj2 = MyClass(100, "World");
3. 拷贝构造函数
class MyClass {
private:
    int value;
    std::string name;
    
public:
    // 拷贝构造函数
    MyClass(const MyClass& other) {
        value = other.value;
        name = other.name;
        std::cout << "Copy constructor called" << std::endl;
    }
};

// 使用
MyClass obj1(42, "Original");
MyClass obj2 = obj1;  // 调用拷贝构造函数
MyClass obj3(obj1);   // 调用拷贝构造函数
4. 移动构造函数(C++11)
class MyClass {
private:
    int* data;
    size_t size;
    
public:
    // 移动构造函数
    MyClass(MyClass&& other) noexcept {
        // "窃取"资源
        data = other.data;
        size = other.size;
        
        // 置空原对象
        other.data = nullptr;
        other.size = 0;
        
        std::cout << "Move constructor called" << std::endl;
    }
};

初始化列表

class Student {
private:
    std::string name;
    int age;
    const int id;  // 常量成员
    int& ref;      // 引用成员
    
public:
    // 使用初始化列表(推荐)
    Student(const std::string& n, int a, int studentId, int& r) 
        : name(n), age(a), id(studentId), ref(r) {
        std::cout << "Student constructor called" << std::endl;
    }
    
    // 错误示例:不能在构造函数体内初始化常量和引用
    // Student(const std::string& n, int a, int studentId, int& r) {
    //     name = n;
    //     age = a;
    //     id = studentId;  // 错误!常量必须在初始化列表中初始化
    //     ref = r;         // 错误!引用必须在初始化列表中初始化
    // }
};

析构函数(Destructor)

什么是析构函数?

析构函数是一种特殊的成员函数,在对象销毁时自动调用,用于清理资源。

析构函数的特点

  1. 类名前加 ~
  2. 没有参数
  3. 没有返回类型
  4. 不能重载
  5. 自动调用

析构函数示例

class MyClass {
private:
    int* data;
    size_t size;
    
public:
    // 构造函数
    MyClass(size_t s) : size(s) {
        data = new int[size];  // 动态分配内存
        std::cout << "Constructor: allocated " << size << " integers" << std::endl;
    }
    
    // 析构函数
    ~MyClass() {
        delete[] data;  // 释放内存
        std::cout << "Destructor: freed " << size << " integers" << std::endl;
    }
};

// 使用
void test() {
    MyClass obj(100);  // 构造函数调用
    // ... 使用 obj
}  // obj 离开作用域,析构函数自动调用

完整示例

资源管理类示例

#include <iostream>
#include <cstring>

class String {
private:
    char* data;
    size_t length;
    
public:
    // 默认构造函数
    String() : data(nullptr), length(0) {
        std::cout << "Default constructor" << std::endl;
    }
    
    // 参数化构造函数
    String(const char* str) {
        length = std::strlen(str);
        data = new char[length + 1];
        std::strcpy(data, str);
        std::cout << "Parameterized constructor: " << data << std::endl;
    }
    
    // 拷贝构造函数
    String(const String& other) {
        length = other.length;
        data = new char[length + 1];
        std::strcpy(data, other.data);
        std::cout << "Copy constructor: " << data << std::endl;
    }
    
    // 移动构造函数(C++11)
    String(String&& other) noexcept {
        // 窃取资源
        data = other.data;
        length = other.length;
        
        // 置空原对象
        other.data = nullptr;
        other.length = 0;
        
        std::cout << "Move constructor" << std::endl;
    }
    
    // 拷贝赋值运算符
    String& operator=(const String& other) {
        if (this != &other) {  // 防止自赋值
            delete[] data;  // 释放原有资源
            
            length = other.length;
            data = new char[length + 1];
            std::strcpy(data, other.data);
        }
        std::cout << "Copy assignment: " << data << std::endl;
        return *this;
    }
    
    // 移动赋值运算符(C++11)
    String& operator=(String&& other) noexcept {
        if (this != &other) {
            delete[] data;  // 释放原有资源
            
            // 窃取资源
            data = other.data;
            length = other.length;
            
            // 置空原对象
            other.data = nullptr;
            other.length = 0;
        }
        std::cout << "Move assignment" << std::endl;
        return *this;
    }
    
    // 析构函数
    ~String() {
        delete[] data;
        std::cout << "Destructor" << std::endl;
    }
    
    // 其他成员函数
    const char* c_str() const { return data ? data : ""; }
    size_t size() const { return length; }
};

// 使用示例
int main() {
    std::cout << "=== 创建对象 ===" << std::endl;
    String s1;                    // 默认构造函数
    String s2 = "Hello";          // 参数化构造函数
    String s3 = s2;               // 拷贝构造函数
    
    std::cout << "\n=== 赋值操作 ===" << std::endl;
    s1 = s3;                      // 拷贝赋值
    s1 = String("World");         // 移动赋值
    
    std::cout << "\n=== 函数调用 ===" << std::endl;
    {
        String temp = "Temporary"; // 参数化构造函数
    } // temp 离开作用域,调用析构函数
    
    std::cout << "\n=== 程序结束 ===" << std::endl;
    return 0;
} // s1, s2, s3 离开作用域,调用析构函数

输出结果

=== 创建对象 ===
Default constructor
Parameterized constructor: Hello
Copy constructor: Hello

=== 赋值操作 ===
Copy assignment: Hello
Parameterized constructor: World
Move assignment
Destructor

=== 函数调用 ===
Parameterized constructor: Temporary
Destructor

=== 程序结束 ===
Destructor
Destructor
Destructor

特殊成员函数规则

Rule of Three/Five/Zero

Rule of Three(C++98/03)

如果一个类需要以下之一,通常需要全部三个:

  • 析构函数
  • 拷贝构造函数
  • 拷贝赋值运算符
Rule of Five(C++11及以后)

如果类管理资源,应该考虑定义全部五个特殊成员函数:

  • 析构函数
  • 拷贝构造函数
  • 拷贝赋值运算符
  • 移动构造函数
  • 移动赋值运算符
Rule of Zero

理想情况下,类不应该自己管理资源,而是使用智能指针等RAII类,这样编译器生成的默认函数就足够了。

// Rule of Zero 示例
class GoodClass {
private:
    std::unique_ptr<int[]> data;  // 使用智能指针管理资源
    std::string name;             // 使用标准库类
    
public:
    // 不需要定义析构函数、拷贝/移动构造函数、拷贝/移动赋值运算符
    // 编译器生成的默认函数就足够好
    
    GoodClass(const std::string& n) : name(n) {}
};

虚析构函数

class Base {
public:
    Base() { std::cout << "Base constructor" << std::endl; }
    virtual ~Base() { std::cout << "Base destructor" << std::endl; }  // 虚析构函数
};

class Derived : public Base {
public:
    Derived() { std::cout << "Derived constructor" << std::endl; }
    ~Derived() override { std::cout << "Derived destructor" << std::endl; }
};

// 使用
Base* ptr = new Derived();
delete ptr;  // 正确调用 Derived 的析构函数,因为有虚析构函数

总结

  • 构造函数:对象创建时调用,用于初始化
  • 析构函数:对象销毁时调用,用于清理资源
  • 重要规则
    • 使用初始化列表初始化成员
    • 管理资源的类需要遵循 Rule of Five
    • 基类应该使用虚析构函数
    • 优先使用 Rule of Zero

构造函数和析构函数是C++中实现RAII(资源获取即初始化)模式的基础,对于资源管理和异常安全至关重要。

c++中的构造函数,如果类比python语言的话,在python中相当于什么?

C++ 构造函数与 Python 的类比

基本对应关系

C++ 概念 Python 对应 说明
构造函数 __init__ 方法 对象初始化
析构函数 __del__ 方法 对象清理
默认构造函数 无参数的 __init__ 创建默认对象
拷贝构造函数 __init__ + 手动拷贝 对象拷贝
移动构造函数 无直接对应 Python 使用引用计数

详细对比

1. 基本构造函数 ↔ __init__ 方法

C++:

class Person {
private:
    std::string name;
    int age;
    
public:
    // 构造函数
    Person(const std::string& n, int a) : name(n), age(a) {
        std::cout << "Person created: " << name << std::endl;
    }
};

// 使用
Person p("Alice", 25);

Python:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        print(f"Person created: {self.name}")

# 使用
p = Person("Alice", 25)

2. 默认构造函数 ↔ 无参数 __init__

C++:

class MyClass {
public:
    MyClass() {
        std::cout << "Default constructor" << std::endl;
    }
};

MyClass obj;  // 调用默认构造函数

Python:

class MyClass:
    def __init__(self):
        print("Default constructor")

obj = MyClass()  # 调用 __init__

3. 析构函数 ↔ __del__ 方法

C++:

class FileHandler {
private:
    FILE* file;
    
public:
    FileHandler(const char* filename) {
        file = fopen(filename, "r");
        std::cout << "File opened" << std::endl;
    }
    
    ~FileHandler() {
        if (file) {
            fclose(file);
            std::cout << "File closed" << std::endl;
        }
    }
};

// 自动调用析构函数
{
    FileHandler fh("test.txt");
    // 使用文件
} // 离开作用域,自动调用析构函数

Python:

class FileHandler:
    def __init__(self, filename):
        self.file = open(filename, 'r')
        print("File opened")
    
    def __del__(self):
        if self.file:
            self.file.close()
            print("File closed")

# 使用(注意:Python的__del__调用时机不确定)
fh = FileHandler("test.txt")
# 当对象被垃圾回收时调用 __del__

4. 拷贝行为对比

C++ 拷贝构造函数:

class Vector {
private:
    int* data;
    size_t size;
    
public:
    Vector(size_t s) : size(s) {
        data = new int[size];
    }
    
    // 拷贝构造函数
    Vector(const Vector& other) : size(other.size) {
        data = new int[size];
        std::copy(other.data, other.data + size, data);
        std::cout << "Copy constructor called" << std::endl;
    }
    
    ~Vector() {
        delete[] data;
    }
};

Vector v1(10);
Vector v2 = v1;  // 调用拷贝构造函数

Python 的拷贝行为:

class Vector:
    def __init__(self, size):
        self.size = size
        self.data = [0] * size
    
    # Python 没有直接的拷贝构造函数
    # 需要手动实现或使用 copy 模块

v1 = Vector(10)
v2 = v1  # 这只是引用赋值,两个变量指向同一个对象
v3 = Vector(v1.size)  # 手动创建新对象
v3.data = v1.data[:]  # 手动拷贝数据

# 或者使用 copy 模块
import copy
v4 = copy.deepcopy(v1)

重要差异

1. 内存管理方式不同

C++ - 手动管理,确定性析构:

{
    Person p("John", 30);  // 构造函数调用
    // 使用 p
} // p 离开作用域,析构函数立即调用

Python - 自动垃圾回收,非确定性析构:

def test():
    p = Person("John", 30)  # __init__ 调用
    # 使用 p
    # __del__ 可能在函数结束后调用,也可能不立即调用

2. 拷贝语义不同

C++ - 值语义,默认深拷贝:

Vector v1(10);
Vector v2 = v1;  // 深拷贝,两个独立对象

Python - 引用语义,默认浅拷贝:

v1 = Vector(10)
v2 = v1  # 引用拷贝,两个变量指向同一个对象

3. 构造方式不同

C++ - 多种构造方式:

Person p1;              // 默认构造
Person p2("Alice", 25); // 直接构造
Person p3 = p2;         // 拷贝构造
Person p4 = Person("Bob", 30);  // 临时对象 + 移动构造

Python - 统一使用 __init__

p1 = Person()           # 需要定义 __init__(self)
p2 = Person("Alice", 25)
p3 = p2                 # 引用赋值,不是拷贝
p4 = Person(p2.name, p2.age)  # 手动创建新对象

Python 中模拟 C++ 构造函数特性

模拟重载构造函数

Python 使用默认参数和类方法模拟:

class Date:
    def __init__(self, day=1, month=1, year=2000):
        self.day = day
        self.month = month
        self.year = year
    
    @classmethod
    def from_string(cls, date_str):
        """模拟重载构造函数 - 从字符串创建"""
        day, month, year = map(int, date_str.split('/'))
        return cls(day, month, year)
    
    @classmethod
    def from_timestamp(cls, timestamp):
        """模拟重载构造函数 - 从时间戳创建"""
        # 实现时间戳转换逻辑
        return cls(1, 1, 2000)  # 简化示例

# 使用不同的"构造函数"
d1 = Date()                    # 默认构造
d2 = Date(15, 12, 2023)       # 参数化构造
d3 = Date.from_string("25/12/2023")  # 工厂方法
d4 = Date.from_timestamp(1700000000)

模拟 RAII (资源获取即初始化)

Python 使用上下文管理器模拟:

# C++ RAII 风格
class FileHandler:
    def __init__(self, filename, mode='r'):
        self.file = open(filename, mode)
        print(f"File {filename} opened")
    
    def __enter__(self):
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.file.close()
        print("File closed")
    
    def read(self):
        return self.file.read()

# 使用(类似C++的确定性析构)
with FileHandler("test.txt") as fh:
    content = fh.read()
    # 离开 with 块自动关闭文件

实际示例对比

完整的类设计对比

C++:

class BankAccount {
private:
    std::string owner;
    double balance;
    static int accountCount;  // 静态成员
    
public:
    // 构造函数
    BankAccount(const std::string& name, double initialBalance = 0.0) 
        : owner(name), balance(initialBalance) {
        accountCount++;
        std::cout << "Account created for " << owner << std::endl;
    }
    
    // 拷贝构造函数
    BankAccount(const BankAccount& other) 
        : owner(other.owner + " (copy)"), balance(other.balance) {
        accountCount++;
        std::cout << "Account copied" << std::endl;
    }
    
    // 析构函数
    ~BankAccount() {
        accountCount--;
        std::cout << "Account for " << owner << " destroyed" << std::endl;
    }
    
    // 成员函数
    void deposit(double amount) {
        balance += amount;
    }
    
    static int getAccountCount() {
        return accountCount;
    }
};

int BankAccount::accountCount = 0;

Python:

class BankAccount:
    _account_count = 0  # 类变量(类似静态成员)
    
    def __init__(self, name, initial_balance=0.0):
        self.owner = name
        self.balance = initial_balance
        BankAccount._account_count += 1
        print(f"Account created for {self.owner}")
    
    def __del__(self):
        BankAccount._account_count -= 1
        print(f"Account for {self.owner} destroyed")
    
    def deposit(self, amount):
        self.balance += amount
    
    @classmethod
    def get_account_count(cls):
        return cls._account_count
    
    # Python 没有内置的拷贝构造,但可以添加一个方法
    @classmethod
    def from_existing(cls, existing_account, new_name):
        """模拟拷贝构造"""
        new_account = cls(new_name, existing_account.balance)
        return new_account

总结

  • __init__ ≈ 构造函数:都用于对象初始化
  • __del__ ≈ 析构函数:都用于清理,但调用时机不同
  • 主要差异
    • C++ 有确定的构造/析构时机,Python 依赖垃圾回收
    • C++ 默认值语义,Python 默认引用语义
    • C++ 支持构造函数重载,Python 使用默认参数和类方法模拟
    • C++ 有专门的拷贝/移动构造函数,Python 需要手动处理

理解这些差异有助于在两种语言间转换思维,但要注意它们的设计哲学和内存管理机制有很大不同。

(注:文档部分内容可能由 AI 生成)

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐