1. C++类与对象核心概念回顾
在上一期内容中,我们探讨了C++类和对象的基础概念,包括类的定义、成员变量和成员函数的声明。今天我们将深入探讨构造函数、析构函数、this指针等核心机制。这些概念是理解C++面向对象编程的关键所在。
构造函数就像房屋建造时的施工蓝图,它决定了对象创建时的初始化方式。而析构函数则相当于拆除队伍,负责对象生命周期结束时的清理工作。理解这些机制对编写健壮的C++代码至关重要。
2. 构造函数与初始化列表详解
2.1 构造函数类型与特性
构造函数是特殊的成员函数,在创建对象时自动调用。它没有返回类型,名称与类名相同。C++支持多种构造函数形式:
cpp复制class Person {
public:
// 默认构造函数
Person() : age(0), name("Unknown") {}
// 带参数构造函数
Person(int a, const string& n) : age(a), name(n) {}
// 拷贝构造函数
Person(const Person& other) : age(other.age), name(other.name) {}
private:
int age;
string name;
};
注意:如果类中没有定义任何构造函数,编译器会自动生成默认构造函数。但一旦定义了任何构造函数,编译器就不会再生成默认构造函数。
2.2 初始化列表的优势
初始化列表是构造函数的重要组成部分,它位于构造函数参数列表后,以冒号开头。使用初始化列表有三大优势:
- 效率更高:对于类类型成员,直接调用拷贝构造函数而非先默认构造再赋值
- 必须使用的情况:const成员和引用成员必须在初始化列表中初始化
- 初始化顺序:成员变量的初始化顺序只与它们在类中的声明顺序有关
cpp复制class Student {
public:
Student(int id, const string& n)
: studentID(id), // 基本类型
name(n), // string类
coursesTaken(0) // 直接初始化
{}
private:
const int studentID;
string name;
int coursesTaken;
};
3. 析构函数与资源管理
3.1 析构函数基础
析构函数在对象销毁时自动调用,用于释放资源。它的名称是在类名前加波浪线(~),没有参数和返回值。
cpp复制class FileHandler {
public:
FileHandler(const char* filename) {
file = fopen(filename, "r");
if (!file) {
throw runtime_error("Failed to open file");
}
}
~FileHandler() {
if (file) {
fclose(file);
cout << "File closed successfully" << endl;
}
}
private:
FILE* file;
};
3.2 RAII原则应用
Resource Acquisition Is Initialization (RAII)是C++的核心编程范式。其核心思想是:
- 资源获取即初始化:在构造函数中获取资源
- 资源释放即销毁:在析构函数中释放资源
这种技术确保了异常安全,即使程序抛出异常,析构函数也会被调用,资源得以释放。
cpp复制void processFile() {
FileHandler fh("data.txt"); // 构造函数打开文件
// 使用文件...
// 函数结束时,fh的析构函数自动关闭文件
}
4. this指针深度解析
4.1 this指针的本质
this指针是类的非静态成员函数的隐式参数,指向调用该成员函数的对象实例。它有以下特点:
- 类型为
ClassName* const(常量指针) - 只能在非静态成员函数内部使用
- 由编译器自动传递和管理
cpp复制class Counter {
public:
void increment() {
++count; // 等价于 ++this->count
}
Counter& add(int value) {
count += value;
return *this; // 返回对象本身的引用
}
private:
int count = 0;
};
4.2 链式调用模式
通过返回*this引用,可以实现方法的链式调用,这是流式接口的基础:
cpp复制Counter c;
c.add(5).add(3).add(2); // 链式调用
5. 类的高级特性
5.1 const成员函数
const成员函数承诺不会修改对象状态,可以在const对象上调用:
cpp复制class Rectangle {
public:
int area() const { // const成员函数
return width * height;
}
void resize(int w, int h) { // 非const成员函数
width = w;
height = h;
}
private:
int width, height;
};
void printArea(const Rectangle& r) {
cout << r.area(); // 正确:调用const成员函数
// r.resize(10, 20); // 错误:不能在const对象上调用非const成员函数
}
5.2 static成员
static成员属于类本身而非对象实例,所有对象共享同一份static成员:
cpp复制class Employee {
public:
Employee(const string& n) : name(n) {
++totalEmployees; // 每次创建对象时递增
}
~Employee() {
--totalEmployees; // 每次销毁对象时递减
}
static int getTotal() { // static成员函数
return totalEmployees;
}
private:
string name;
static int totalEmployees; // static成员变量
};
// 必须在类外定义和初始化static成员变量
int Employee::totalEmployees = 0;
6. 实战案例:简单的银行账户系统
让我们用一个完整的例子来综合运用所学知识:
cpp复制class BankAccount {
public:
// 构造函数
BankAccount(const string& owner, double initialBalance = 0.0)
: ownerName(owner), balance(initialBalance), accountNumber(++nextAccountNumber) {}
// 存款
void deposit(double amount) {
if (amount <= 0) {
throw invalid_argument("Deposit amount must be positive");
}
balance += amount;
transactions.push_back("Deposit: +" + to_string(amount));
}
// 取款
bool withdraw(double amount) {
if (amount <= 0) {
throw invalid_argument("Withdrawal amount must be positive");
}
if (amount > balance) {
return false;
}
balance -= amount;
transactions.push_back("Withdrawal: -" + to_string(amount));
return true;
}
// 打印账户信息
void printStatement() const {
cout << "Account #" << accountNumber << endl;
cout << "Owner: " << ownerName << endl;
cout << "Balance: " << balance << endl;
cout << "Transaction History:" << endl;
for (const auto& t : transactions) {
cout << " " << t << endl;
}
}
// 静态成员函数
static int getNextAccountNumber() {
return nextAccountNumber;
}
private:
string ownerName;
double balance;
vector<string> transactions;
int accountNumber;
static int nextAccountNumber; // 静态成员变量
};
// 初始化静态成员变量
int BankAccount::nextAccountNumber = 1000; // 起始账号
7. 常见问题与调试技巧
7.1 构造函数中的异常处理
构造函数中抛出异常时,对象构造被视为失败,不会调用析构函数。因此需要特别注意资源管理:
cpp复制class ResourceHolder {
public:
ResourceHolder() : ptr(new int[100]) {
if (someCondition) {
delete[] ptr; // 必须手动释放
throw runtime_error("Construction failed");
}
}
~ResourceHolder() { delete[] ptr; }
private:
int* ptr;
};
更好的做法是使用智能指针来自动管理资源。
7.2 对象切片问题
当派生类对象被赋值给基类对象时,会发生对象切片,丢失派生类特有的部分:
cpp复制class Base { /*...*/ };
class Derived : public Base { /*...*/ };
Derived d;
Base b = d; // 对象切片,丢失Derived特有部分
解决方案是使用指针或引用:
cpp复制Base& b = d; // 通过引用访问,不会切片
7.3 const正确性
保持const正确性可以避免许多潜在错误:
cpp复制class DataProcessor {
public:
void process() const {
// readData(); // 正确:const成员函数可以调用其他const成员函数
// modifyData(); // 错误:const成员函数不能调用非const成员函数
}
void readData() const { /*...*/ }
void modifyData() { /*...*/ }
};
8. 性能优化建议
8.1 移动语义的应用
C++11引入的移动语义可以显著提升性能:
cpp复制class Buffer {
public:
// 移动构造函数
Buffer(Buffer&& other) noexcept
: data(other.data), size(other.size) {
other.data = nullptr; // 确保源对象处于有效状态
other.size = 0;
}
// 移动赋值运算符
Buffer& operator=(Buffer&& other) noexcept {
if (this != &other) {
delete[] data; // 释放现有资源
data = other.data;
size = other.size;
other.data = nullptr;
other.size = 0;
}
return *this;
}
private:
int* data;
size_t size;
};
8.2 返回值优化(RVO)
现代编译器支持返回值优化,可以避免不必要的拷贝:
cpp复制Matrix createMatrix() {
Matrix m(100, 100);
// 初始化矩阵...
return m; // 编译器可能会应用RVO,避免拷贝
}
Matrix mat = createMatrix(); // 可能直接构造在mat中
9. 现代C++特性应用
9.1 委托构造函数
C++11允许构造函数调用同类中的其他构造函数:
cpp复制class Time {
public:
Time() : Time(0, 0, 0) {} // 委托给三参数构造函数
Time(int h) : Time(h, 0, 0) {} // 委托给三参数构造函数
Time(int h, int m, int s)
: hour(h), minute(m), second(s)
{
normalize();
}
private:
int hour, minute, second;
void normalize() { /*...*/ }
};
9.2 默认和删除函数
可以显式指定使用默认实现或删除某些特殊成员函数:
cpp复制class NonCopyable {
public:
NonCopyable() = default;
~NonCopyable() = default;
// 禁止拷贝
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete;
// 允许移动
NonCopyable(NonCopyable&&) = default;
NonCopyable& operator=(NonCopyable&&) = default;
};
10. 设计模式中的类应用
10.1 单例模式实现
使用静态成员和私有构造函数实现单例:
cpp复制class Logger {
public:
static Logger& getInstance() {
static Logger instance; // C++11保证线程安全
return instance;
}
void log(const string& message) {
// 记录日志...
}
private:
Logger() = default; // 私有构造函数
~Logger() = default;
// 禁止拷贝和赋值
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
};
// 使用方式
Logger::getInstance().log("System started");
10.2 工厂方法模式
通过静态成员函数创建对象:
cpp复制class Shape {
public:
virtual ~Shape() = default;
virtual void draw() const = 0;
static unique_ptr<Shape> create(const string& type);
};
class Circle : public Shape { /*...*/ };
class Rectangle : public Shape { /*...*/ };
unique_ptr<Shape> Shape::create(const string& type) {
if (type == "circle") return make_unique<Circle>();
if (type == "rectangle") return make_unique<Rectangle>();
throw invalid_argument("Unknown shape type");
}
