Effective C++基础¶
📖 章节简介¶
本章将介绍Effective C++的基础概念,包括构造/析构、资源管理和拷贝控制。
上图串联了对象从构造到析构的完整生命周期,强调 RAII 与拷贝/移动控制的核心作用。
🏗️ 构造与析构¶
1. 构造函数¶
C++
// 构造函数
#include <iostream>
#include <string>
using namespace std;
class Person {
private:
string name;
int age;
public:
// 默认构造函数
Person() : name("匿名"), age(0) {
cout << "默认构造函数被调用" << endl;
}
// 参数化构造函数
Person(string n, int a) : name(n), age(a) {
cout << "参数化构造函数被调用" << endl;
}
// 拷贝构造函数
Person(const Person& other) : name(other.name), age(other.age) {
cout << "拷贝构造函数被调用" << endl;
}
// 移动构造函数
Person(Person&& other) : name(std::move(other.name)), age(other.age) {
cout << "移动构造函数被调用" << endl;
}
// 委托构造函数
Person(string n, int a, [[maybe_unused]] string p) : Person(n, a) {
cout << "委托构造函数被调用" << endl;
}
// 析构函数
~Person() {
cout << "析构函数被调用: " << name << endl;
}
void display() const {
cout << "姓名: " << name << ", 年龄: " << age << endl;
}
};
int main() {
// 默认构造
Person person1;
person1.display();
// 参数化构造
Person person2("张三", 25);
person2.display();
// 拷贝构造
Person person3 = person2;
person3.display();
// 移动构造
Person person4 = std::move(person3);
person4.display();
return 0;
}
2. 构造函数初始化列表¶
C++
// 初始化列表
#include <iostream>
#include <string>
using namespace std;
class Point {
private:
int x;
int y;
public:
// 初始化列表(C++11)
Point(int x = 0, int y = 0) : x(x), y(y) {
cout << "初始化列表: x=" << x << ", y=" << y << endl;
}
// 委托构造函数使用初始化列表
Point(string pointStr) : Point() {
// 解析字符串 "x,y"
size_t commaPos = pointStr.find(',');
x = stoi(pointStr.substr(0, commaPos));
y = stoi(pointStr.substr(commaPos + 1));
}
void display() const {
cout << "坐标: (" << x << ", " << y << ")" << endl;
}
};
int main() {
Point point1(10, 20);
point1.display();
Point point2("30,40");
point2.display();
return 0;
}
🔒 资源管理¶
1. RAII模式¶
C++
// RAII模式
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
class FileHandler {
private:
ofstream file;
public:
FileHandler(const string& filename) : file(filename) {
if (!file.is_open()) {
cout << "无法打开文件: " << filename << endl;
}
}
~FileHandler() {
if (file.is_open()) {
file.close();
cout << "文件已关闭" << endl;
}
}
void write(const string& content) {
if (file.is_open()) {
file << content;
}
}
};
int main() {
{
FileHandler handler("test.txt");
handler.write("Hello, RAII!");
// 文件自动在作用域结束时关闭
}
cout << "文件已自动关闭" << endl;
return 0;
}
2. 智能指针¶
C++
// 智能指针
#include <iostream>
#include <memory>
using namespace std;
class Resource {
private:
int* data;
int size;
public:
Resource(int size) : size(size) {
data = new int[size]();
cout << "资源分配" << endl;
}
~Resource() {
delete[] data;
cout << "资源释放" << endl;
}
void display() const {
for (int i = 0; i < size; i++) {
cout << data[i] << " ";
}
cout << endl;
}
};
int main() {
// ✅ 好的做法:使用智能指针
unique_ptr<Resource> resource = make_unique<Resource>(10);
resource->display();
// 资源自动释放
// ❌ 不好的做法:手动管理内存
Resource* rawResource = new Resource(10);
rawResource->display();
// delete rawResource; // 容易忘记
return 0;
}
📋 拷贝控制¶
1. 拷贝构造函数¶
C++
// 拷贝控制 - 深拷贝示例
#include <iostream>
#include <string>
using namespace std;
class Buffer {
private:
int* data;
size_t size;
public:
// 构造函数
Buffer(size_t s) : size(s) {
data = new int[size];
for (size_t i = 0; i < size; i++) {
data[i] = 0;
}
cout << "构造函数" << endl;
}
// 深拷贝构造函数
Buffer(const Buffer& other) : size(other.size) {
data = new int[size];
for (size_t i = 0; i < size; i++) {
data[i] = other.data[i];
}
cout << "深拷贝构造函数" << endl;
}
// 深拷贝赋值运算符
Buffer& operator=(const Buffer& other) {
if (this != &other) {
delete[] data;
size = other.size;
data = new int[size];
for (size_t i = 0; i < size; i++) {
data[i] = other.data[i];
}
cout << "赋值运算符(深拷贝)" << endl;
}
return *this;
}
~Buffer() {
delete[] data;
cout << "析构函数" << endl;
}
// 访问器方法
int& operator[](size_t index) {
return data[index];
}
const int& operator[](size_t index) const {
return data[index];
}
void display() const {
for (size_t i = 0; i < size; i++) {
cout << data[i] << " ";
}
cout << endl;
}
};
int main() {
Buffer buffer1(10);
for (int i = 0; i < 10; i++) {
buffer1[i] = i;
}
buffer1.display();
// 深拷贝
Buffer buffer2 = buffer1;
buffer2.display();
// 修改buffer2不会影响buffer1(深拷贝)
buffer2[0] = 100;
cout << "修改buffer2后:" << endl;
cout << "buffer1: ";
buffer1.display();
cout << "buffer2: ";
buffer2.display();
return 0;
}
2. 移动语义¶
C++
// 移动语义
#include <iostream>
#include <utility>
using namespace std;
class BigData {
private:
int* data;
size_t size;
public:
BigData(size_t s) : size(s) {
data = new int[size];
for (size_t i = 0; i < size; i++) {
data[i] = i;
}
cout << "构造函数: 分配" << size << "个元素" << endl;
}
// 拷贝构造函数(深拷贝)
BigData(const BigData& other) : size(other.size) {
data = new int[size];
for (size_t i = 0; i < size; i++) {
data[i] = other.data[i];
}
cout << "拷贝构造函数: 深拷贝" << endl;
}
// 移动构造函数
BigData(BigData&& other) noexcept : data(other.data), size(other.size) {
other.data = nullptr;
other.size = 0;
cout << "移动构造函数: 窃取资源" << endl;
}
// 移动赋值运算符
BigData& operator=(BigData&& other) noexcept {
if (this != &other) {
delete[] data;
data = other.data;
size = other.size;
other.data = nullptr;
other.size = 0;
cout << "移动赋值运算符: 窃取资源" << endl;
}
return *this;
}
// 拷贝赋值运算符
BigData& operator=(const BigData& other) {
if (this != &other) {
delete[] data;
size = other.size;
data = new int[size];
for (size_t i = 0; i < size; i++) {
data[i] = other.data[i];
}
cout << "拷贝赋值运算符: 深拷贝" << endl;
}
return *this;
}
~BigData() {
delete[] data;
cout << "析构函数: 释放" << size << "个元素" << endl;
}
void display() const {
for (size_t i = 0; i < size; i++) {
cout << data[i] << " ";
}
cout << endl;
}
};
int main() {
BigData data1(10);
data1.display();
// 移动构造
BigData data2 = std::move(data1);
data2.display();
// data1已经被移动,不应该再使用
// data1.display(); // 未定义行为
return 0;
}
💡 最佳实践¶
1. 遵循Rule of Five¶
C++
// Rule of Five
int main() {
// ✅ 好的做法:遵循Rule of Five
class RuleOfFive {
public:
RuleOfFive() = default;
// 析构函数
~RuleOfFive() = default;
// 拷贝构造函数
RuleOfFive(const RuleOfFive&) = default;
// 拷贝赋值运算符
RuleOfFive& operator=(const RuleOfFive&) = default;
// 移动构造函数
RuleOfFive(RuleOfFive&&) noexcept = default;
// 移动赋值运算符
RuleOfFive& operator=(RuleOfFive&&) noexcept = default;
};
// ❌ 不好的做法:不遵循Rule of Five
class BadClass {
public:
BadClass() = default;
~BadClass() = default;
// 没有拷贝构造函数
// 拷贝赋值运算符可能有问题
// 没有移动构造函数
// 移动赋值运算符可能有问题
};
return 0;
}
2. 资源管理¶
C++
// 资源管理
int main() {
// ✅ 好的做法:使用RAII
class GoodClass {
private:
int* data;
public:
GoodClass(int size) : data(new int[size]) {}
~GoodClass() {
delete[] data;
cout << "资源自动释放" << endl;
}
};
// ❌ 不好的做法:不使用RAII
class BadClass {
private:
int* data;
public:
BadClass(int size) {
data = new int[size];
}
// 忘记释放内存
~BadClass() {
// delete[] data; // 容易忘记
}
};
return 0;
}
📝 练习题¶
基础题¶
- 什么是RAII模式?
- 拷贝和移动有什么区别?
- Rule of Three是什么?
进阶题¶
- 实现一个资源管理类。
- 正确实现拷贝语义。
- 使用智能指针管理资源。
实践题¶
- 创建一个文件处理类。
- 实现一个智能指针包装器。
- 构建一个资源池。
📚 推荐阅读¶
🔗 下一章¶
Effective C++进阶 - 学习Effective C++的进阶内容。