在C++编程中,类和对象是最基础也是最核心的概念。类可以理解为一种自定义的数据类型,它封装了数据(成员变量)和操作这些数据的方法(成员函数)。而对象则是类的具体实例,就像"汽车"是一个类,而"我的红色宝马"就是一个具体的对象。
初学者最容易混淆的就是类和对象的关系。打个比方,类就像是建筑设计图纸,而对象就是根据这张图纸建造出来的实际房子。图纸定义了房子的结构和功能,但只有实际建造出来的房子才能住人。
cpp复制class Car { // 类定义
public:
string color;
string brand;
void honk() {
cout << "Beep beep!" << endl;
}
};
int main() {
Car myCar; // 创建对象
myCar.color = "red";
myCar.brand = "BMW";
myCar.honk(); // 调用成员函数
return 0;
}
构造函数是类中一种特殊的成员函数,它在创建对象时自动调用。构造函数的主要作用是初始化对象的状态。如果没有显式定义构造函数,编译器会提供一个默认的无参构造函数。
cpp复制class Student {
public:
string name;
int age;
// 构造函数
Student(string n, int a) {
name = n;
age = a;
cout << "Student对象已创建" << endl;
}
};
int main() {
Student s1("张三", 18); // 调用构造函数
return 0;
}
注意:构造函数没有返回类型,且名称必须与类名完全相同。它可以有参数,也可以重载。
析构函数是另一个特殊成员函数,它在对象生命周期结束时自动调用,通常用于释放资源。析构函数的名称是在类名前加波浪号(~),且没有参数和返回值。
cpp复制class FileHandler {
public:
FILE* file;
FileHandler(const char* filename) {
file = fopen(filename, "r");
if (!file) {
cerr << "文件打开失败" << endl;
}
}
~FileHandler() {
if (file) {
fclose(file);
cout << "文件已关闭" << endl;
}
}
};
this指针是一个隐含的指针,它指向当前对象的地址。在类的非静态成员函数中,编译器会自动添加this指针作为函数的第一个参数。
cpp复制class Example {
public:
int value;
void setValue(int value) {
this->value = value; // 使用this区分成员变量和参数
}
};
cpp复制class Calculator {
public:
int result;
Calculator& add(int num) {
result += num;
return *this; // 返回当前对象引用
}
Calculator& subtract(int num) {
result -= num;
return *this;
}
};
int main() {
Calculator calc;
calc.add(5).subtract(3).add(10); // 链式调用
return 0;
}
静态成员变量属于类本身,而不是类的某个对象。所有对象共享同一个静态成员变量。必须在类外进行定义和初始化。
cpp复制class Counter {
public:
static int count; // 声明静态成员
Counter() {
count++;
}
};
int Counter::count = 0; // 定义并初始化静态成员
int main() {
Counter c1, c2, c3;
cout << "对象数量: " << Counter::count << endl; // 输出3
return 0;
}
静态成员函数只能访问静态成员变量,不能访问非静态成员。它可以通过类名直接调用,不需要对象实例。
cpp复制class MathUtils {
public:
static int square(int x) {
return x * x;
}
};
int main() {
cout << MathUtils::square(5) << endl; // 输出25
return 0;
}
友元函数不是类的成员函数,但可以访问类的私有成员。需要在类内用friend关键字声明。
cpp复制class Box {
private:
double width;
public:
friend void printWidth(Box box); // 友元函数声明
Box(double w) : width(w) {}
};
void printWidth(Box box) {
cout << "宽度: " << box.width << endl; // 可以访问私有成员
}
友元类的所有成员函数都可以访问另一个类的私有成员。这在某些紧密协作的类之间很有用。
cpp复制class A {
private:
int secret;
friend class B; // 声明B为友元类
};
class B {
public:
void showSecret(A& a) {
cout << "A的秘密是: " << a.secret << endl;
}
};
注意:友元关系不具有传递性(A的朋友的朋友不一定是A的朋友)和继承性(父类的朋友不一定是子类的朋友)。
对象数组的每个元素都是一个对象,可以像普通数组一样使用。
cpp复制class Point {
public:
int x, y;
Point(int x = 0, int y = 0) : x(x), y(y) {}
};
int main() {
Point points[3] = {Point(1,2), Point(3,4), Point()};
for (int i = 0; i < 3; i++) {
cout << "点" << i << ": (" << points[i].x
<< ", " << points[i].y << ")" << endl;
}
return 0;
}
对象指针指向一个对象,通过箭头运算符(->)访问成员。
cpp复制class Person {
public:
string name;
void introduce() {
cout << "我是" << name << endl;
}
};
int main() {
Person p1;
Person* ptr = &p1;
ptr->name = "李四";
ptr->introduce(); // 输出"我是李四"
return 0;
}
在构造函数中分配内存时,一定要在析构函数中释放,避免内存泄漏。
cpp复制class StringWrapper {
private:
char* str;
public:
StringWrapper(const char* s) {
str = new char[strlen(s) + 1];
strcpy(str, s);
}
~StringWrapper() {
delete[] str; // 必须释放内存
}
};
默认的拷贝构造函数和赋值运算符执行的是浅拷贝,对于包含指针成员的类,这可能导致问题。
cpp复制class ShallowCopyExample {
public:
int* data;
ShallowCopyExample(int size) {
data = new int[size];
}
~ShallowCopyExample() {
delete[] data;
}
// 需要自定义拷贝构造函数和赋值运算符
ShallowCopyExample(const ShallowCopyExample& other) {
// 深拷贝实现
data = new int[/*大小*/];
// 复制数据
}
ShallowCopyExample& operator=(const ShallowCopyExample& other) {
if (this != &other) {
// 深拷贝实现
}
return *this;
}
};
静态成员的初始化顺序是不确定的,如果静态成员之间有依赖关系,可能会导致问题。
解决方案:
cpp复制class Logger {
public:
static Logger& instance() {
static Logger logger; // 线程安全的单例(C++11)
return logger;
}
void log(const string& message) {
// 日志实现
}
private:
Logger() {} // 私有构造函数
};
让我们用一个完整的例子来综合运用前面学到的知识。
cpp复制#include <iostream>
#include <string>
using namespace std;
class BankAccount {
private:
string owner;
double balance;
static int totalAccounts; // 静态成员,记录总账户数
public:
BankAccount(string name, double initial = 0.0)
: owner(name), balance(initial) {
totalAccounts++;
}
~BankAccount() {
totalAccounts--;
}
void deposit(double amount) {
if (amount > 0) {
balance += amount;
cout << "存款成功,当前余额: " << balance << endl;
}
}
bool withdraw(double amount) {
if (amount > 0 && balance >= amount) {
balance -= amount;
cout << "取款成功,当前余额: " << balance << endl;
return true;
}
cout << "取款失败,余额不足" << endl;
return false;
}
void display() const {
cout << "账户持有人: " << owner
<< ", 余额: " << balance << endl;
}
static int getTotalAccounts() {
return totalAccounts;
}
// 转账功能(友元函数)
friend bool transfer(BankAccount& from, BankAccount& to, double amount);
};
int BankAccount::totalAccounts = 0; // 静态成员初始化
bool transfer(BankAccount& from, BankAccount& to, double amount) {
if (from.withdraw(amount)) {
to.deposit(amount);
return true;
}
return false;
}
int main() {
BankAccount acc1("张三", 1000);
BankAccount acc2("李四", 500);
acc1.deposit(200);
acc2.withdraw(100);
transfer(acc1, acc2, 300);
cout << "总账户数: " << BankAccount::getTotalAccounts() << endl;
return 0;
}
这个案例展示了:
在构造函数中,使用初始化列表比在构造函数体内赋值更高效,特别是对于类类型成员。
cpp复制class Example {
private:
string name;
int value;
public:
// 使用初始化列表
Example(string n, int v) : name(n), value(v) {}
// 比下面的方式更高效
// Example(string n, int v) {
// name = n;
// value = v;
// }
};
不修改对象状态的成员函数应该声明为const,这可以提高代码的安全性和可读性。
cpp复制class Rectangle {
private:
double width, height;
public:
double area() const { // const成员函数
return width * height;
}
};
对于管理资源的类,实现移动构造函数和移动赋值运算符可以显著提高性能。
cpp复制class ResourceHolder {
private:
int* data;
size_t size;
public:
// 移动构造函数
ResourceHolder(ResourceHolder&& other) noexcept
: data(other.data), size(other.size) {
other.data = nullptr;
other.size = 0;
}
// 移动赋值运算符
ResourceHolder& operator=(ResourceHolder&& other) noexcept {
if (this != &other) {
delete[] data;
data = other.data;
size = other.size;
other.data = nullptr;
other.size = 0;
}
return *this;
}
};
可以显式要求编译器生成默认实现或删除某些函数。
cpp复制class NonCopyable {
public:
NonCopyable() = default;
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete;
};
提高代码的清晰度和安全性,明确表达设计意图。
cpp复制class Base {
public:
virtual void foo() const {
cout << "Base::foo" << endl;
}
virtual ~Base() = default;
};
class Derived : public Base {
public:
void foo() const override { // 明确表示重写
cout << "Derived::foo" << endl;
}
void bar() final { // 禁止进一步重写
cout << "Derived::bar" << endl;
}
};
使用智能指针可以简化资源管理,避免内存泄漏。
cpp复制#include <memory>
class SmartExample {
private:
std::unique_ptr<int[]> data; // 独占所有权
std::shared_ptr<std::string> info; // 共享所有权
public:
SmartExample(size_t size)
: data(std::make_unique<int[]>(size)),
info(std::make_shared<std::string>("示例")) {}
// 不需要自定义析构函数
};
在实际开发中,我发现合理使用现代C++特性可以显著减少错误,提高代码质量。特别是在资源管理方面,智能指针几乎消除了手动内存管理的需要。对于初学者来说,建议从基础开始,逐步掌握这些高级特性。