【学会到精通C++】【5】面向对象编程精髓 - 类与对象
本文深入讲解了C++面向对象编程中类与对象的核心概念。主要内容包括:1)类与对象的关系及OOP三大支柱(封装、继承、多态);2)类设计要点,包括访问控制、this指针机制;3)构造函数类型及成员初始化列表的使用;4)析构函数与RAII资源管理机制;5)特殊成员函数规则(Rule of Three/Five/Zero);6)静态成员的使用。文章通过大量代码示例展示了如何正确设计类、管理对象生命周期,
·
学会到精通C++:面向对象编程精髓 - 类与对象 (5/100)
核心目标:深入理解C++面向对象编程的核心机制,掌握类设计原则与对象生命周期管理,为构建健壮、高效的软件系统奠定基础。
一、面向对象编程核心思想
1. 类与对象的关系
- 类 (Class):用户自定义的抽象数据类型蓝图
- 对象 (Object):类的具体实例(内存中的实体)
// 类定义:汽车蓝图
class Car {
// 成员变量(属性)
std::string brand;
int year;
double fuelLevel;
// 成员函数(方法)
void accelerate();
void refuel(double amount);
};
// 对象实例化:具体汽车
Car myCar; // 栈上对象
Car* companyCar = new Car(); // 堆上对象
2. OOP三大支柱
- 封装 (Encapsulation):隐藏实现细节,暴露接口
- 继承 (Inheritance):创建层次化的类关系(下篇重点)
- 多态 (Polymorphism):同一接口不同实现(下下篇重点)
二、类设计深度解析
1. 访问控制与封装
访问修饰符 | 类内 | 派生类 | 类外 |
---|---|---|---|
private |
✅ | ❌ | ❌ |
protected |
✅ | ✅ | ❌ |
public |
✅ | ✅ | ✅ |
最佳实践:
- 数据成员通常设为
private
- 提供
public
接口进行受控访问 - 使用
protected
为派生类保留扩展点
class BankAccount {
private:
double balance; // 封装数据
public:
// 受控访问接口
double getBalance() const { return balance; }
void deposit(double amount) {
if(amount > 0) balance += amount;
}
};
2. this
指针机制
- 每个非静态成员函数隐含的
this
参数 - 指向调用该成员函数的对象
- 底层实现:编译器将
obj.method()
转换为method(&obj)
class Counter {
int count = 0;
public:
void increment() {
this->count++; // 显式使用this
// 等效于 count++;
}
Counter* getAddress() {
return this; // 返回当前对象地址
}
};
三、构造函数与对象初始化
1. 构造函数类型
类型 | 特点 | 调用时机 |
---|---|---|
默认构造函数 | 无参数 | Car c; |
参数化构造函数 | 带参数 | Car c("Toyota", 2020); |
拷贝构造函数 | const T& 参数 |
Car c2 = c1; |
移动构造函数 | T&& 参数 (C++11) |
Car c3 = std::move(c1); |
2. 成员初始化列表(关键!)
class Engine {
public:
Engine(int hp) : horsepower(hp) {} // 正确初始化
private:
const int horsepower; // const成员必须初始化
};
class Car {
public:
// 使用初始化列表
Car(std::string br, int yr, int hp)
: brand(br), year(yr), engine(hp) // 效率更高
{
// 构造函数体
}
private:
std::string brand;
int year;
Engine engine; // 成员对象
};
必须使用初始化列表的场景:
const
成员变量- 引用成员变量
- 没有默认构造函数的成员对象
- 基类初始化(继承时)
3. 委托构造函数 (C++11)
class Rectangle {
public:
// 主构造函数
Rectangle(int w, int h) : width(w), height(h) {}
// 委托给主构造函数
Rectangle() : Rectangle(10, 10) {}
// 正方形委托
Rectangle(int size) : Rectangle(size, size) {}
};
四、析构函数与资源管理
1. 析构函数特性
- 名称:
~ClassName()
- 无参数、无返回值
- 负责释放对象占用的资源
- 调用时机:对象生命周期结束时
class FileHandler {
public:
FileHandler(const char* filename)
: file(fopen(filename, "r"))
{
if(!file) throw std::runtime_error("File open failed");
}
~FileHandler() {
if(file) fclose(file); // RAII资源释放
}
private:
FILE* file;
};
2. RAII (Resource Acquisition Is Initialization)
- 核心思想:资源获取即初始化
- 资源生命周期绑定对象生命周期
- 使用智能指针(后续详解)实现自动管理
五、特殊成员函数与规则
1. Rule of Three/Five/Zero
规则 | 适用版本 | 需定义函数 |
---|---|---|
Rule of Three | C++98 | 析构函数、拷贝构造、拷贝赋值 |
Rule of Five | C++11 | 增加移动构造、移动赋值 |
Rule of Zero | 现代C++ | 使用智能指针/容器管理资源 |
// Rule of Five 示例
class DynamicArray {
public:
// 1. 构造函数
DynamicArray(size_t size) : size(size), data(new int[size]) {}
// 2. 析构函数
~DynamicArray() { delete[] data; }
// 3. 拷贝构造函数
DynamicArray(const DynamicArray& other)
: size(other.size), data(new int[other.size])
{
std::copy(other.data, other.data + size, data);
}
// 4. 拷贝赋值运算符
DynamicArray& operator=(const DynamicArray& other) {
if (this != &other) {
delete[] data;
size = other.size;
data = new int[size];
std::copy(other.data, other.data + size, data);
}
return *this;
}
// 5. 移动构造函数 (C++11)
DynamicArray(DynamicArray&& other) noexcept
: size(other.size), data(other.data)
{
other.size = 0;
other.data = nullptr;
}
// 6. 移动赋值运算符 (C++11)
DynamicArray& operator=(DynamicArray&& other) noexcept {
if (this != &other) {
delete[] data;
size = other.size;
data = other.data;
other.size = 0;
other.data = nullptr;
}
return *this;
}
private:
size_t size;
int* data;
};
2. = default
与 = delete
class DefaultExample {
public:
DefaultExample() = default; // 显式使用默认实现
~DefaultExample() = default;
// 禁用拷贝
DefaultExample(const DefaultExample&) = delete;
DefaultExample& operator=(const DefaultExample&) = delete;
// 禁用移动
DefaultExample(DefaultExample&&) = delete;
DefaultExample& operator=(DefaultExample&&) = delete;
};
六、静态成员与类级操作
1. 静态成员变量
- 类范围内共享的变量
- 必须在类外单独定义(除C++17内联静态变量)
class Employee {
public:
Employee() { ++count; }
~Employee() { --count; }
static int getCount() { return count; }
private:
static int count; // 声明
};
int Employee::count = 0; // 类外定义
2. 静态成员函数
- 无
this
指针 - 只能访问静态成员
- 可通过类名直接调用
class MathUtils {
public:
static double pi() { return 3.1415926535; }
static int max(int a, int b) { return a > b ? a : b; }
};
// 使用
double circleArea = MathUtils::pi() * radius * radius;
3. 单例模式实现(现代线程安全版)
class Database {
public:
// 删除拷贝和移动操作
Database(const Database&) = delete;
Database& operator=(const Database&) = delete;
Database(Database&&) = delete;
Database& operator=(Database&&) = delete;
// 获取单例实例
static Database& instance() {
static Database db; // C++11保证线程安全
return db;
}
void connect() { /* ... */ }
void query(const std::string& sql) { /* ... */ }
private:
Database() { /* 私有构造函数 */ }
~Database() = default;
};
// 使用
Database::instance().connect();
七、类与结构体的区别
特性 | class |
struct |
---|---|---|
默认访问权限 | private | public |
使用场景 | 有行为的对象 | 数据聚合(POD类型) |
继承默认 | private继承 | public继承 |
模板元编程 | 常用 | 较少使用 |
现代C++建议:
- 需要方法/私有状态时用
class
- 纯数据集合时用
struct
- 与C兼容的数据结构用
struct
八、类设计最佳实践
- 遵循单一职责原则:每个类只负责一件事
- 优先组合而非继承:降低耦合度
- 使用const正确性:
class Vector { public: // const成员函数:承诺不修改对象状态 int size() const { return size_; } // 非const成员函数 void push_back(int value) { /* ... */ } };
- 明智使用
mutable
:class Cache { mutable std::mutex mtx; // 可在const函数中修改 mutable std::vector<int> cachedData; public: int getData() const { std::lock_guard<std::mutex> lock(mtx); // 需要修改mutex if (cachedData.empty()) { // 填充缓存... } return cachedData.back(); } };
九、对象生命周期与内存管理
1. 对象创建位置
位置 | 生命周期管理 | 访问方式 |
---|---|---|
栈 (Stack) | 自动销毁 | 直接访问 |
堆 (Heap) | 手动管理(new/delete) | 指针访问 |
静态存储区 | 程序运行期 | 全局/静态 |
2. 对象数组
// 栈上对象数组
Car parkingLot[10];
// 堆上对象数组
Car* fleet = new Car[50];
delete[] fleet; // 必须使用delete[]
十、实战:设计一个现代字符串类
class MyString {
public:
// 构造函数
MyString() : data(nullptr), length(0) {}
explicit MyString(const char* str);
// 规则五
~MyString();
MyString(const MyString& other);
MyString(MyString&& other) noexcept;
MyString& operator=(const MyString& other);
MyString& operator=(MyString&& other) noexcept;
// 成员函数
size_t size() const { return length; }
const char* c_str() const { return data ? data : ""; }
private:
char* data;
size_t length;
void copyFrom(const char* src, size_t len);
};
// 实现略(见完整代码示例)
总结与进阶方向
关键知识点:
- 类设计与封装原则
- 构造函数与初始化列表
- 析构函数与RAII模式
- 特殊成员函数与规则五
- 静态成员与单例模式
this
指针机制- 对象生命周期管理
性能提示:
- 优先使用初始化列表
- 移动语义减少拷贝开销
- 小对象在栈上创建
noexcept
移动操作
下篇预告:第6篇《继承的艺术:构建类层次结构》
- 继承类型与访问控制
- 虚函数机制与vtable
- 抽象类与接口设计
- 多重继承与钻石问题
“C++中的类不是一种抽象机制,而是一种具体化机制。” —— Bjarne Stroustrup
练习任务:
- 实现完整的MyString类(含移动语义)
- 设计线程安全的日志类(单例模式)
- 创建表示几何图形的类层次(为继承铺垫)
更多推荐
所有评论(0)