1. 类与对象简介
1.1 什么是类和对象
- 类(Class)是C++中创建用户自定义类型的一种方式,它将数据(成员变量)和操作数据的函数(成员函数)封装在一起。
- 对象(Object)是类的实例化,拥有类定义的所有属性和行为。
- 类更像是汽车图纸,对象更像是造出来的汽车。
1.2 类的作用
- 封装(Encapsulation):将数据和操作数据的代码绑定在一起,保护数据不被外界直接访问。
- 抽象(Abstraction):通过类定义抽象出具有共同特性的对象,提高代码的可重用性和可维护性。
- 继承(Inheritance)和多态(Polymorphism):实现代码的复用与动态绑定。
2. 类的定义
2.1 基本语法
1 | class ClassName { |
2.2 示例
创建一个表示学生的类:
1 |
|
3. 成员变量与成员函数
3.1 成员变量
- 成员变量(Member Variables):用于存储对象的状态信息。
- 命名约定:常用下划线结尾(例如
name_
)表示成员变量,避免与局部变量混淆。
3.2 成员函数
- 成员函数(Member Functions):定义对象的行为,可以访问和修改成员变量。
- 常成员函数(Const Member Functions):保证函数不会修改对象的状态。
3.3 示例实现
1 | // Student.cpp |
4. 访问控制
4.1 访问修饰符
- public:公有成员,可以被所有代码访问。
- private:私有成员,仅能被类的成员函数和友元访问。
- protected:受保护成员,仅能被类的成员函数、友元和派生类访问。
4.2 例子
1 | class Sample { |
5. 构造函数与析构函数
5.1 构造函数
- 默认构造函数:没有参数的构造函数。
- 参数化构造函数:接受参数以初始化对象。
- 拷贝构造函数:用一个对象初始化另一个对象。
- 移动构造函数(C++11):从临时对象“移动”资源。
5.2 析构函数
- 析构函数(Destructor):在对象生命周期结束时调用,用于释放资源。
5.3 示例
1 |
|
5.4 使用示例
1 | int main() { |
输出示例:
1 | Default constructor called. |
5.5 拷贝构造是否必须实现
当一个类A中有成员变量是另一个类类型B的时候,有时候拷贝构造会失效。比如一个类A中有成员变量std::thread
,std::thread
没有构造函数,所以A类的拷贝构造无法合成,需要显示编写。
同样析构也要显示编写,等待线程完成。
除此之外我们可以自己实现拷贝构造,进而实现浅拷贝和深拷贝的不同效果
5.6 构造顺序和析构顺序
类A中包含成员变量是类B的类型,如果是先调用A的构造还是B的构造呢?
如果析构的时候是A先析构还是B先析构呢?
5.7 类默认构造是否必须实现
如果类中有继承关系或者其他类型的成员,默认构造函数是很有必要实现的。
系统提供的合成的默认构造函数不会对成员做初始化操作。
5.8 this
指针的特性和用途
指向当前对象:
this
指针是一个隐式参数,指向调用成员函数的对象。通过this
,你可以访问当前对象的属性和方法。
区分成员变量和参数:
在构造函数或成员函数中,参数名和成员变量可能同名。使用
1
this
可以明确指代成员变量。例如:
1
2
3
4
5
6
7
8class MyClass {
private:
int value;
public:
MyClass(int value) {
this->value = value; // 使用 this 指针区分成员变量和参数
}
};
返回当前对象:
- ```
this1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
可以用于返回当前对象的引用,以支持链式调用。例如:
```cpp
class MyClass {
private:
int value;
public:
MyClass& setValue(int value) {
this->value = value;
return *this; // 返回当前对象的引用
}
};
MyClass obj;
obj.setValue(10).setValue(20); // 链式调用
- ```
在 const 成员函数中的使用:
- 在
const
成员函数中,this
的类型为const MyClass*
,这意味着你不能通过this
修改成员变量。这有助于确保对象的状态不被改变。
- 在
在静态成员函数中的不可用性:
- 静态成员函数没有
this
指针,因为它们不属于任何特定对象,而是属于类本身。因此,静态成员函数不能访问非静态成员变量和成员函数。
- 静态成员函数没有
示例代码
以下是一个简单的示例,展示了 this
指针的用法:
1 |
|
5.9 delete和default
C++11
用法:
delete可以删除指定的构造函数。
default可以指定某个构造函数为系统默认合成。
6. 拷贝控制
拷贝构造函数与拷贝赋值运算符
6.1 拷贝构造函数
- 定义:用于创建一个新对象,并复制现有对象的成员。
- 语法:
ClassName(const ClassName& other);
6.2 拷贝赋值运算符
- 定义:用于将一个已有对象的值赋给另一个已有对象。
- 语法:
ClassName& operator=(const ClassName& other);
6.3 示例
1 |
|
6.4 使用示例
1 | int main() { |
输出示例:
1 | Constructor called. |
7. 移动语义
7.1 什么是移动语义
- 移动语义(Move Semantics):允许资源的所有权从一个对象转移到另一个对象,避免不必要的拷贝,提高性能。
7.2 移动构造函数与移动赋值运算符
- 移动构造函数:
ClassName(ClassName&& other) noexcept;
- 移动赋值运算符:
ClassName& operator=(ClassName&& other) noexcept;
7.3 示例
1 |
|
7.4 使用示例
1 | int main() { |
输出示例:
1 | Constructor called. |
8. 类的友元
8.1 什么是友元
- 友元(Friend):可以访问类的私有和保护成员的非成员函数或另一个类。
8.2 类型
- 友元函数:单个函数可以被声明为友元。
- 友元类:整个类可以被声明为友元。
8.3 使用示例
1 |
|
8.4 使用友元类
1 | class Rectangle { |
9. 运算符重载
9.1 什么是运算符重载
- 运算符重载(Operator Overloading):允许对自定义类型使用C++运算符,如
+
,-
,<<
等。
9.2 重载运算符的规则
- 只能对已有运算符进行重载,不能创建新运算符。
- 至少有一个操作数必须是用户定义的类型。
- 不能改变运算符的优先级或结合性。
9.3 示例:重载 +
运算符
1 |
|
9.4 示例:重载 <<
运算符(输出流)
1 |
|
输出示例:
1 | Employee Name: John Doe, Salary: $75000 |
10. 练习示例
项目:实现自定义MyString
类
目标:创建一个简单的MyString
类,支持拷贝构造,默认构造,有参构造,支持输出和比较等。
1 |
|
代码说明
- 私有成员:
char* data
:指向动态分配的字符数组,用于存储字符串。
- 构造函数:
- 默认构造函数:初始化
data
为nullptr
。 - 有参构造函数:接收一个
const char*
类型的字符串,动态分配内存并复制字符串内容。 - 拷贝构造函数:复制另一个
MyString
对象的内容,确保深拷贝。
- 默认构造函数:初始化
- 赋值运算符重载:支持将一个
MyString
对象赋值给另一个,确保释放原有内存并进行深拷贝。 - 比较运算符重载:支持比较两个
MyString
对象是否相等。 - 输出运算符重载:支持直接使用
std::cout
输出MyString
对象。 - 析构函数:释放动态分配的内存,防止内存泄漏。
使用示例
在 main
函数中,创建了几个 MyString
对象,演示了拷贝构造、赋值和比较的用法。
视频教程
关于C++的视频教程可参考我的主页