类的具体设计:数据成员、成员函数与模板
在了解了头文件的防卫式声明之后,现在我们要真正动笔设计一个类了。本篇以复数为例,手把手带你搭建一个完整的类:从决定类里装什么数据(实部、虚部),到设计操作这些数据的函数(如取共轭),如果实部、虚部的类型想从 double 换成 int 时,难道要重写一遍类吗?C++ 为此提供了模板机制。
class 关键字与类的作用域
任何一个类,无论多复杂,其最外层的结构都是固定的。由 class 关键字引导,后跟类名,接着用花括号 { } 划定类的作用域,最后以分号结束。
1 | class 类名 { |
初学 C++ 时,最容易遗漏的就是类定义末尾的分号。这和函数、循环等结构不同,花括号后不加分号会导致编译错误。将类定义视为一种“类型声明”,就像声明结构体一样,末尾必须加分号。
数据成员
数据成员是类的“属性”,用来存储对象的状态。以复数类为例,它需要两个数据成员:实部与虚部,通常用 double 类型表示。
复数类的数据成员:
1 | class Complex { |
设计要点:
- 数据成员通常设为 private(默认就是 private)
- 通过成员函数来访问和修改
- 命名风格:常用下划线或 m_ 前缀
类的默认访问权限是 private,但很多人误以为默认是 public。如果忘记写 public:,外部代码无法直接访问数据成员。建议在类定义中明确写出访问说明符,避免混淆。
成员函数
成员函数定义了对象可以执行的操作,是类的“行为”。对于复数类,常见的操作包括取共轭、加法、输出等。成员函数可以在类内定义(自动成为 inline),也可以在类外通过作用域解析运算符 :: 定义。
共轭函数(类内定义):
1 | class Complex { |
运算符重载(类外定义)
1 | Complex operator+(const Complex& a, |
友元(friend)
如果希望非成员函数(如运算符重载)直接访问类的私有成员,可以将它声明为友元。友元函数不是类的成员函数,但可以访问所有私有成员。使用需谨慎,它会破坏封装性。
1 | class Complex { |
const 成员函数表示该函数不会修改对象的数据成员,是良好的设计习惯。对于只读的访问函数(如 real()、imag()),务必加上 const。
类的通用化:模板(Template)
如果复数类的实部和虚部需要支持 int、float、double 等多种类型,难道要复制粘贴多份代码吗?C++ 的类模板可以解决这个问题:将数据类型参数化,使用时再指定具体类型。
模板类定义:
1 | template <typename T> |
使用示例:
1 | Complex<double> c1(1.0, 2.0); |
模板的声明和定义通常需要放在同一个头文件中(或包含 .tpp 文件),不能像普通函数那样将声明放 .h、定义放 .cpp,否则链接时会出现“未定义引用”错误。这是因为模板在实例化时需要完整的定义。
模板参数不限于类型,也可以是非类型参数,如整数或指针。例如,可以定义一个固定长度的数组类:
template <typename T, int N> class Array { … };
使用时:Array<double, 10> arr;





