1. C++面向对象编程基础概念
在C++编程中,类和对象是面向对象编程(OOP)的核心概念。类可以看作是一个蓝图或模板,它定义了对象的属性和行为。当我们谈论"盒子"这个概念时,类就是描述盒子应该具有哪些特征(如长、宽、高)和能做什么(如计算体积)。
cpp复制class Box {
public:
double length; // 长度
double breadth; // 宽度
double height; // 高度
};
这段代码定义了一个简单的Box类,包含三个公共成员变量。public关键字表示这些成员可以在类的外部直接访问。在实际开发中,我们通常会将这些成员设为private,通过公共方法来访问,这就是封装的基本思想。
2. 类的封装原理与实践
封装是OOP的三大特性之一,它隐藏了对象的内部实现细节,只暴露必要的接口。让我们改进上面的Box类,实现更好的封装:
cpp复制class Box {
private:
double length;
double breadth;
double height;
public:
// 设置尺寸
void setDimensions(double l, double b, double h) {
if(l <= 0 || b <= 0 || h <= 0) {
throw std::invalid_argument("尺寸必须为正数");
}
length = l;
breadth = b;
height = h;
}
// 获取体积
double getVolume() const {
return length * breadth * height;
}
// 获取单个尺寸
double getLength() const { return length; }
double getBreadth() const { return breadth; }
double getHeight() const { return height; }
};
这个改进版本中:
- 成员变量设为private,外部无法直接访问
- 提供了公共方法来设置和获取尺寸
- 添加了参数验证,确保尺寸为正数
- const成员函数保证不修改对象状态
注意:良好的封装可以防止对象处于无效状态,并允许未来修改内部实现而不影响使用该类的代码。
3. 对象实例化详解
实例化是创建类的具体对象的过程。C++中有几种不同的实例化方式:
3.1 栈上实例化
cpp复制Box box1; // 默认构造
Box box2(1,2,3); // 带参数构造
Box box3 = box2; // 拷贝构造
栈上对象的生命周期与其作用域相关,离开作用域时自动销毁。
3.2 堆上实例化
cpp复制Box* pBox1 = new Box(); // 默认构造
Box* pBox2 = new Box(1,2,3); // 带参数构造
// 使用后必须手动释放
delete pBox1;
delete pBox2;
堆上对象需要手动管理内存,忘记释放会导致内存泄漏。
3.3 初始化陷阱
cpp复制Box box(); // 这是函数声明,不是对象创建!
Box box; // 这才是创建默认构造的对象
这个常见陷阱会让初学者困惑,因为带空括号的声明实际上是一个函数声明而非对象创建。
4. this指针深度解析
this指针是C++类成员函数中一个隐含的参数,它指向调用该成员函数的对象实例。理解this指针对掌握C++面向对象编程至关重要。
4.1 this指针的本质
每个非静态成员函数都有一个隐藏的this参数,编译器会自动添加。例如:
cpp复制void Box::setDimensions(double l, double b, double h) {
this->length = l;
this->breadth = b;
this->height = h;
}
编译器实际上将其处理为:
cpp复制void Box_setDimensions(Box* this, double l, double b, double h) {
this->length = l;
this->breadth = b;
this->height = h;
}
4.2 this指针的典型应用场景
- 解决命名冲突:
cpp复制void Box::setLength(double length) {
this->length = length; // 区分成员变量和参数
}
- 链式调用:
cpp复制class Box {
public:
Box& setLength(double l) { length = l; return *this; }
Box& setBreadth(double b) { breadth = b; return *this; }
Box& setHeight(double h) { height = h; return *this; }
};
// 使用
Box box;
box.setLength(1).setBreadth(2).setHeight(3);
- 返回对象自身:
cpp复制class Box {
public:
Box* getThis() { return this; }
};
4.3 this指针的特殊情况
- 静态成员函数没有this指针,因为它们不与特定对象关联
- const成员函数中的this指针是const的,不能用于修改对象
- volatile成员函数中的this指针是volatile的
5. 类与结构体的区别
虽然C++中class和struct很相似,但它们有几个关键区别:
| 特性 | class | struct |
|---|---|---|
| 默认访问权限 | private | public |
| 默认继承方式 | private | public |
| 用于模板参数 | 可以 | 不可以 |
| 初始化方式 | 复杂类型需构造函数 | 可用聚合初始化 |
cpp复制struct Point { int x; int y; };
Point p = {1, 2}; // 聚合初始化,class默认不行
class PointClass {
int x; int y;
public:
PointClass(int x, int y) : x(x), y(y) {}
};
PointClass pc(1, 2); // 必须通过构造函数
实际建议:用struct表示纯数据结构,用class表示具有行为的对象。但这不是强制规则,更多是编程风格的选择。
6. 常见问题与解决方案
6.1 对象拷贝问题
cpp复制Box b1;
b1.setDimensions(1,2,3);
Box b2 = b1; // 浅拷贝
默认的拷贝构造函数执行浅拷贝,如果类中有指针成员,这会导致问题。解决方案是实现深拷贝:
cpp复制class Box {
int* data; // 动态分配的内存
public:
Box(const Box& other) {
data = new int[10];
std::copy(other.data, other.data+10, data);
}
// 还需要重载赋值运算符
Box& operator=(const Box& other) {
if(this != &other) {
delete[] data;
data = new int[10];
std::copy(other.data, other.data+10, data);
}
return *this;
}
};
6.2 const正确性
cpp复制class Box {
double volume;
public:
double getVolume() const {
// volume = 0; // 错误:const成员函数不能修改成员变量
return volume;
}
};
void printVolume(const Box& box) {
cout << box.getVolume(); // 只有const成员函数能在const对象上调用
}
6.3 对象生命周期管理
cpp复制{
Box box1; // 构造函数被调用
Box* pBox = new Box(); // 堆分配
} // box1析构函数被调用,但pBox指向的内存泄漏
// 解决方案1:手动删除
delete pBox;
// 解决方案2:使用智能指针
std::unique_ptr<Box> smartBox(new Box());
7. 高级技巧与最佳实践
7.1 返回*this实现方法链
cpp复制class Box {
double l, b, h;
public:
Box& setL(double length) { l = length; return *this; }
Box& setB(double breadth) { b = breadth; return *this; }
Box& setH(double height) { h = height; return *this; }
};
Box box;
box.setL(1).setB(2).setH(3); // 链式调用
7.2 使用this实现自注册模式
cpp复制class Observer;
class Subject {
std::vector<Observer*> observers;
public:
void registerObserver(Observer* obs);
};
class Observer {
Subject* subject;
public:
Observer(Subject* subj) : subject(subj) {
subject->registerObserver(this); // 使用this自注册
}
};
7.3 基于this的类型转换
cpp复制class Base {
public:
virtual void log() { cout << "Base\n"; }
void callLog() { this->log(); } // 动态绑定
};
class Derived : public Base {
public:
void log() override { cout << "Derived\n"; }
};
Derived d;
d.callLog(); // 输出"Derived",因为this是Derived*
在实际项目中,理解类和对象的工作原理以及this指针的行为,可以帮助你编写更健壮、更灵活的面向对象代码。记住,良好的封装和适当的接口设计是构建可维护系统的关键。
