在C++编程中,类和对象是最基础也是最重要的概念之一。类可以看作是一个蓝图或模板,它定义了一类对象的属性和行为。而对象则是根据这个蓝图创建的具体实例。
类的定义使用class关键字,后跟类名和一对大括号。类中可以包含两种主要成员:
cpp复制class Stack {
public:
// 成员函数
void Push(int x) {
// 实现压栈操作
}
// 成员变量
int* a;
int top;
int capacity;
};
在实际开发中,为了区分成员变量和局部变量,通常会给成员变量添加特殊前缀,如_或m:
cpp复制class Date {
public:
void Init(int year, int month, int day) {
_year = year;
_month = month;
_day = day;
}
private:
int _year; // 使用下划线前缀
int _month;
int _day;
};
C++通过访问限定符实现封装,这是面向对象编程的重要特性:
cpp复制class Stack {
private: // 私有成员
int* _array;
size_t _capacity;
size_t _top;
public: // 公有接口
void Init(int n = 4) {
_array = (int*)malloc(sizeof(int) * n);
_capacity = n;
_top = 0;
}
int Top() {
return _array[_top - 1];
}
};
最佳实践:通常将成员变量设为private,只暴露必要的成员函数作为公有接口。这样可以防止外部代码随意修改对象内部状态,提高程序的健壮性。
类只是定义,不占用实际内存空间。只有当实例化对象时,才会分配内存:
cpp复制Date d1; // 实例化Date对象d1
Date d2; // 实例化Date对象d2
对象在内存中的布局只包含成员变量,成员函数代码存放在代码段,被所有对象共享。这样可以节省内存空间,避免每个对象都保存一份函数代码。
对象的大小由成员变量决定,遵循内存对齐规则:
cpp复制class Example {
char c; // 1字节
int i; // 4字节
double d; // 8字节
};
// sizeof(Example) = 16 (考虑对齐)
注意:空类的大小为1字节,这是为了确保每个对象都有唯一的地址。
this指针是编译器自动添加的隐含参数,指向当前对象实例:
cpp复制class Date {
public:
void Init(int year, int month, int day) {
// 编译器实际处理为:
// this->_year = year;
_year = year;
_month = month;
_day = day;
}
};
this指针的特点:
类名* const(常量指针)问题1:空指针调用成员函数
cpp复制Date* p = nullptr;
p->Print(); // 可能正常运行
p->_year; // 解引用空指针,崩溃
这是因为成员函数调用是通过固定地址跳转的,不访问对象数据。而访问成员变量需要解引用this指针。
问题2:返回*this实现链式调用
cpp复制class Calculator {
public:
Calculator& add(int x) {
value += x;
return *this;
}
int value = 0;
};
Calculator calc;
calc.add(1).add(2).add(3); // 链式调用
构造函数在对象创建时自动调用,用于初始化对象:
cpp复制class Date {
public:
// 无参构造函数
Date() {
_year = 1;
_month = 1;
_day = 1;
}
// 带参构造函数
Date(int year, int month, int day) {
_year = year;
_month = month;
_day = day;
}
};
初始化列表:更高效的初始化方式
cpp复制Date(int year, int month, int day)
: _year(year), _month(month), _day(day) {
// 函数体
}
析构函数在对象销毁时自动调用,用于释放资源:
cpp复制class Stack {
public:
~Stack() {
free(_array);
_array = nullptr;
}
private:
int* _array;
};
拷贝构造函数:
cpp复制Stack(const Stack& other) {
_array = (int*)malloc(sizeof(int) * other._capacity);
memcpy(_array, other._array, sizeof(int) * other._top);
_capacity = other._capacity;
_top = other._top;
}
赋值运算符重载:
cpp复制Stack& operator=(const Stack& other) {
if (this != &other) { // 防止自赋值
free(_array);
_array = (int*)malloc(sizeof(int) * other._capacity);
memcpy(_array, other._array, sizeof(int) * other._top);
_capacity = other._capacity;
_top = other._top;
}
return *this;
}
经验法则:如果需要自定义析构函数,通常也需要自定义拷贝构造函数和赋值运算符(Rule of Three)。
cpp复制class Date {
public:
bool operator==(const Date& d) const {
return _year == d._year &&
_month == d._month &&
_day == d._day;
}
Date operator+(int days) const {
Date temp(*this);
// 实现日期加法逻辑
return temp;
}
};
cpp复制ostream& operator<<(ostream& out, const Date& d) {
out << d._year << "-" << d._month << "-" << d._day;
return out;
}
istream& operator>>(istream& in, Date& d) {
in >> d._year >> d._month >> d._day;
return in;
}
静态成员属于类而不是对象:
cpp复制class Counter {
public:
static int count; // 声明
Counter() { count++; }
~Counter() { count--; }
static int getCount() { return count; }
};
int Counter::count = 0; // 定义
友元可以突破封装,访问私有成员:
cpp复制class Date {
friend ostream& operator<<(ostream& out, const Date& d);
private:
int _year, _month, _day;
};
ostream& operator<<(ostream& out, const Date& d) {
out << d._year << "-" << d._month << "-" << d._day;
return out;
}
cpp复制class Buffer {
public:
// 移动构造函数
Buffer(Buffer&& other) noexcept
: _data(other._data), _size(other._size) {
other._data = nullptr;
other._size = 0;
}
// 移动赋值运算符
Buffer& operator=(Buffer&& other) noexcept {
if (this != &other) {
delete[] _data;
_data = other._data;
_size = other._size;
other._data = nullptr;
other._size = 0;
}
return *this;
}
private:
int* _data;
size_t _size;
};
cpp复制Date getDate() {
return Date(2024, 1, 1); // 匿名对象
}
// 编译器可能优化为直接构造
在实际开发中,理解类和对象的概念是编写高质量C++代码的基础。从简单的数据封装到复杂的继承多态,这些概念贯穿整个C++面向对象编程的始终。掌握好这些基础知识,才能更好地理解和使用更高级的C++特性。