程设一些知识点
程设一些知识点
知识点
本文档主要整理了我在复习中遇到比较不熟悉的几个知识点,包括运算符重载、虚函数、模板以及智能指针的使用。
1. 运算符重载
运算符重载允许我们为自定义类型(如类)重新定义或“重载”已有的运算符行为。
1.1. 输入/输出运算符重载 (以 complex
类为例)
在 C++ 中,<<
和 >>
运算符通常分别用于输出和输入。我们可以为自定义类重载这些运算符,以便能像内置类型一样方便地进行输入输出操作。
1 |
|
解释:
friend
关键字:允许非成员函数访问类的private
和protected
成员。对于<<
和>>
重载,通常将它们声明为友元函数,因为它们的左操作数是流对象(istream
或ostream
),而不是类对象。istream &operator>>(istream &in, complex &com)
:- 参数
istream &in
: 输入流的引用 (例如cin
)。 - 参数
complex &com
: 要被赋值的complex
对象的引用。 - 返回值
istream &
: 返回输入流的引用,以支持链式输入 (如cin >> c1 >> c2;
)。
- 参数
ostream &operator<<(ostream &out, const complex &com)
:- 参数
ostream &out
: 输出流的引用 (例如cout
)。 - 参数
const complex &com
: 要输出的complex
对象的常量引用(输出操作不应修改对象)。 - 返回值
ostream &
: 返回输出流的引用,以支持链式输出 (如cout << c1 << c2;
)。
- 参数
1.2. 前置与后置自增/自减运算符重载
自增 (++
) 和自减 (--
) 运算符有前置和后置两种形式。它们的重载方式略有不同。
1.2.1. 成员函数方式
前置运算符 (
++obj
或--obj
):- 声明:
T& operator++();
或T& operator--();
- 返回类型通常是被操作对象的引用 (
T&
)。 - 实现时,先修改对象的值,然后返回修改后的对象。
- 声明:
后置运算符 (
obj++
或obj--
):- 声明:
T operator++(int);
或T operator--(int);
- 参数列表中的
int
是一个哑元(dummy argument),仅用于区分前置和后置版本,调用时不需要传递实参。 - 返回类型通常是被操作对象的值 (
T
),而不是引用。这是因为后置操作符需要返回对象在自增/自减前的状态。 - 实现时,先保存对象的当前状态(创建一个临时副本),然后修改原始对象的值,最后返回保存的临时副本。
- 声明:
1.2.2. 全局 (友元) 函数方式
前置运算符 (
++obj
或--obj
):- 声明:
T1& operator++(T2& obj);
或T1& operator--(T2& obj);
(其中T1
通常是T2
或其引用) - 第一个参数是被操作对象的引用。
- 声明:
后置运算符 (
obj++
或obj--
):- 声明:
T1 operator++(T2& obj, int);
或T1 operator--(T2& obj, int);
(其中T1
通常是T2
) - 第一个参数是被操作对象的引用,第二个参数是哑元
int
。
- 声明:
1.2.3. Counter
类示例 (全局函数方式)
1 |
|
要点:
- 后置版本通过一个未命名的
int
参数与前置版本区分。 - 前置版本通常返回对象的引用 (
T&
),而后置版本通常按值返回 (T
),因为它返回的是操作前的对象状态。
2. 虚函数 (Virtual Functions)
虚函数是实现 C++ 运行时多态性的关键机制。当通过基类指针或引用调用派生类中的虚函数时,会根据指针或引用实际指向的对象类型来决定调用哪个版本的函数。
1 |
|
关键点:
virtual ~Base() = default;
: 虚析构函数。如果类可能被继承,并且可能通过基类指针删除派生类对象,则析构函数应声明为virtual
。这确保了派生类的析构函数会被正确调用,防止资源泄漏。= default
表示使用编译器提供的默认实现。virtual int function(int x) const = 0;
: 纯虚函数。virtual
: 表明它是一个虚函数。const
: 表明该函数不会修改类的成员变量(对于const
对象或通过const
引用/指针调用)。= 0
: 将其声明为纯虚函数。纯虚函数在声明它的类中没有定义(实现)。- 包含一个或多个纯虚函数的类称为抽象类。抽象类不能被实例化(不能创建其对象)。
- 派生类必须实现(覆盖)基类中的所有纯虚函数,否则派生类也将成为抽象类。
3. 类模板 (Class Templates)
类模板允许我们定义一个通用的类蓝图,其中的数据类型或某些值可以作为参数在实例化时指定。
1 |
|
解释:
template <typename T, int i>
: 模板声明。typename T
:T
是一个类型参数,代表任何数据类型(如int
,double
,std::string
, 自定义类等)。class
也可以用来代替typename
。int i
:i
是一个非类型模板参数。它必须是一个编译时常量(如整型、指针、引用,或枚举值)。在这里,它决定了buffer
数组的大小。
T buffer[i];
: 类成员,一个类型为T
,大小为i
的数组。T TestClass<T,i>::getData(int j)
: 当在类模板外部定义成员函数时,必须重复模板参数列表,并使用TestClass<T,i>
来限定函数所属的类。
4. 函数模板与智能指针
函数模板允许我们编写一个通用的函数,它可以处理不同类型的数据。智能指针是管理动态分配内存的类,有助于防止内存泄漏。
1 |
|
解释:
template <class T>
:processInput
是一个函数模板,T
是其类型参数。std::unique_ptr<T[]> arr(new T[length]);
(或std::make_unique<T[]>(length);
):std::unique_ptr
是一种智能指针,它拥有其指向的对象。当unique_ptr
对象本身被销毁时(例如离开作用域),它会自动释放所管理的内存。这有助于防止内存泄漏。T[]
表示unique_ptr
管理的是一个T
类型的数组。new T[length]
动态分配一个包含length
个T
类型元素的数组。std::make_unique<T[]>(length)
(C++14+) 是创建unique_ptr
的更安全、更简洁的方式。
arr.get()
:unique_ptr
的get()
成员函数返回一个指向其所管理内存的原始指针。这在需要将管理的内存传递给不接受unique_ptr
的旧式 C API 或函数时很有用。processInput<char>(length, n);
: 这是函数模板的显式实例化。编译器会根据指定的类型参数char
生成一个特定版本的processInput
函数。
5. 构造函数中使用智能指针初始化成员
在类的构造函数中初始化 std::unique_ptr
成员是常见的做法,以确保资源在对象创建时被正确获取,并在对象销毁时自动释放。
1 |
|
解释:
std::unique_ptr<T[]> a;
: 类C1
有一个名为a
的成员,它是一个指向T
类型数组的unique_ptr
。C1(int n) : num(n), a(std::make_unique<T[]>(n)) {}
:- 这是构造函数。
:
num(n),a(std::make_unique<T[]>(n))
是成员初始化列表。这是初始化类成员(尤其是const
成员、引用成员和需要构造函数参数的成员对象)的首选方式。a(std::make_unique<T[]>(n))
初始化unique_ptr
成员a
,使其管理一个新分配的大小为n
的T
类型数组。
- 当
C1
类型的对象被销毁时,其成员a
(即unique_ptr
) 也会被销毁。unique_ptr
的析构函数会自动delete[]
它所管理的数组,从而防止内存泄漏。
程设一些知识点
https://kyc001.github.io/2025/05/24/程设一些知识点/