在C语言中,数据结构和操作它们的函数是分离的。比如要实现一个栈,我们需要单独定义结构体存储数据,再编写独立的函数来操作这个结构体。这种分离的设计会导致几个问题:
C++通过"类"这一概念完美解决了这些问题。类将数据(成员变量)和操作这些数据的方法(成员函数)封装在一起,形成一个完整的逻辑单元。这种封装特性带来了三大优势:
一个典型的类定义包含以下部分:
cpp复制class ClassName {
private:
// 私有成员(通常为数据)
public:
// 公有成员(通常为方法)
protected:
// 受保护成员(继承时使用)
};
以栈的实现为例:
cpp复制class Stack {
private:
int* _array; // 栈空间指针
size_t _capacity; // 栈容量
size_t _top; // 栈顶位置
public:
void Init(int capacity = 4) {
_array = (int*)malloc(sizeof(int) * capacity);
assert(_array);
_capacity = capacity;
_top = 0;
}
void Push(int value) { /* 入栈实现 */ }
int Pop() { /* 出栈实现 */ }
};
在C++中,常见的成员变量命名约定有几种:
_variablemVariablevariable_这些约定都是为了在代码中清晰区分局部变量和成员变量。我个人偏好使用下划线前缀,因为:
类中定义的成员函数默认是内联的(inline),这是C++的一个重要特性。内联函数的特点:
cpp复制// 类内定义(隐式内联)
class Calculator {
public:
int Add(int a, int b) { return a + b; }
};
// 类外定义(显式内联)
class Calculator {
public:
int Add(int a, int b);
};
inline int Calculator::Add(int a, int b) {
return a + b;
}
使用场景:
注意事项:
C++提供三种访问控制级别:
cpp复制class AccessDemo {
public: // 以下成员都是公有的
void PublicFunc() {}
private: // 以下成员都是私有的
int privateVar;
protected: // 以下成员都是受保护的
void ProtectedFunc() {}
};
虽然class和struct在C++中功能几乎相同,但存在几个关键差异:
| 特性 | class | struct |
|---|---|---|
| 默认访问权限 | private | public |
| 继承默认权限 | private | public |
| 文化含义 | 强调封装性 | 强调数据聚合 |
实际编程中的选择建议:
良好的封装应该遵循以下原则:
以银行账户为例:
cpp复制class BankAccount {
private:
double balance; // 隐藏实现细节
public:
// 提供受控的访问接口
void Deposit(double amount) {
if (amount > 0) balance += amount;
}
bool Withdraw(double amount) {
if (amount > 0 && balance >= amount) {
balance -= amount;
return true;
}
return false;
}
double GetBalance() const { return balance; }
};
类定义了一个独立的作用域,所有成员都在这个作用域内。访问类成员时有几种方式:
obj.memberptr->memberClassName::staticMembercpp复制class ScopeDemo {
public:
static int staticVar;
void DemoFunc();
};
int ScopeDemo::staticVar = 0; // 静态成员类外定义
void ScopeDemo::DemoFunc() { // 成员函数类外定义
// 函数实现
}
类实例化对象时,内存分配遵循以下规则:
cpp复制class MemoryDemo {
int x; // 4字节
double y; // 8字节
void func() {} // 不占对象空间
};
// sizeof(MemoryDemo) == 16(考虑对齐)
对象大小的计算规则:
cpp复制class Empty {}; // sizeof == 1
class AlignmentDemo {
char c; // 1字节
int i; // 4字节
double d; // 8字节
};
// sizeof可能是16(取决于对齐设置)
this指针是C++的一个隐式参数,具有以下特点:
ClassName* constconst ClassName* const编译器会将成员函数调用转换为普通函数调用:
cpp复制// 原始代码
obj.func(arg);
// 编译器转换后的等效代码
func(&obj, arg);
this指针在以下情况特别有用:
cpp复制class NameDemo {
int value;
public:
void SetValue(int value) {
this->value = value; // 区分成员和参数
}
};
cpp复制class ChainDemo {
int x, y;
public:
ChainDemo& SetX(int x) { this->x = x; return *this; }
ChainDemo& SetY(int y) { this->y = y; return *this; }
};
// 使用方式
ChainDemo obj;
obj.SetX(10).SetY(20);
cpp复制class SelfReturn {
public:
SelfReturn& GetThis() { return *this; }
};
使用this指针时需要注意:
cpp复制class LambdaDemo {
int data;
public:
void Demo() {
// C++17前
auto lambda1 = [this]() { data = 10; };
// C++17后更安全的做法
auto lambda2 = [*this]() mutable { /* 副本操作 */ };
}
};
未初始化的成员变量:
const正确性问题:
头文件循环引用:
小对象优化:
内联策略:
缓存友好设计:
C++11引入了显式默认和删除特殊成员函数:
cpp复制class ModernDemo {
public:
ModernDemo() = default; // 显式默认构造函数
~ModernDemo() = default; // 显式默认析构函数
// 禁止拷贝
ModernDemo(const ModernDemo&) = delete;
ModernDemo& operator=(const ModernDemo&) = delete;
// 允许移动
ModernDemo(ModernDemo&&) = default;
ModernDemo& operator=(ModernDemo&&) = default;
};
构造函数可以调用同类其他构造函数:
cpp复制class ConstructorDemo {
int x, y;
public:
ConstructorDemo() : ConstructorDemo(0, 0) {} // 委托
ConstructorDemo(int a, int b) : x(a), y(b) {}
};
C++11引入了类内成员初始化:
cpp复制class InitDemo {
int x = 0; // 类内初始化
double y {1.0}; // 统一初始化语法
public:
InitDemo() = default; // 使用类内初始值
InitDemo(int a) : x(a) {} // 部分覆盖
};
在实际工程中,合理运用这些现代特性可以显著提高代码质量和开发效率。理解类的基本概念后,这些高级特性会更容易掌握和应用。