构造函数(constructor)
什么是构造函数?
构造函数是一种特殊的成员函数,在创建类对象时自动调用,用于初始化对象的数据成员。
构造函数的特点
- 与类同名
- 没有返回类型
- 可以重载
- 自动调用
构造函数的类型
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)
什么是析构函数?
析构函数是一种特殊的成员函数,在对象销毁时自动调用,用于清理资源。
析构函数的特点
- 类名前加
~ - 没有参数
- 没有返回类型
- 不能重载
- 自动调用
析构函数示例
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 需要手动处理
理解这些差异有助于在两种语言间转换思维,但要注意它们的设计哲学和内存管理机制有很大不同。
到此这篇关于c++ 构造函数和析构函数示例详解的文章就介绍到这了,更多相关c++ 构造函数和析构函数内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论