C++ 面向对象编程
面向对象编程(OOP)是一种将数据和操作数据的行为封装在一起的编程范式。C++ 是支持 OOP 的主要语言之一,本章将详细介绍类和对象的核心概念。
类与对象基础
什么是类?
类是对一类事物的抽象描述,它定义了这类事物所具有的属性(成员变量)和行为(成员函数):
// 定义一个学生类
class Student {
public: // 公有成员
// 成员变量(属性)
std::string name;
int age;
int score;
// 成员函数(行为)
void display() {
std::cout << "姓名: " << name
<< ", 年龄: " << age
<< ", 成绩: " << score << std::endl;
}
void setScore(int s) {
score = s;
}
};
什么是对象?
对象是类的实例,是具体的一个个体:
int main() {
// 创建对象
Student stu1;
Student stu2;
// 使用对象
stu1.name = "张三";
stu1.age = 20;
stu1.score = 90;
stu2.name = "李四";
stu2.age = 19;
stu2.score = 85;
// 调用对象的方法
stu1.display();
stu2.display();
return 0;
}
类的定义语法
class 类名 {
private:
// 私有成员:只能被类的成员函数访问
// 属性
// 成员函数
public:
// 公有成员:可以被任何代码访问
// 属性
// 成员函数
protected:
// 保护成员:可以被派生类访问
// 属性
// 成员函数
};
访问控制
public、private、protected
class Person {
private: // 私有成员
std::string name; // 外部无法直接访问
int age;
protected: // 保护成员
std::string id; // 派生类可以访问
public: // 公有成员
std::string email; // 外部可以直接访问
// 公有成员函数可以访问所有成员
void setInfo(std::string n, int a) {
name = n;
age = a;
}
void display() {
std::cout << name << " - " << age << std::endl;
}
};
int main() {
Person p;
p.email = "[email protected]"; // 可以访问
// p.name = "张三"; // 错误!私有成员不能直接访问
p.setInfo("张三", 25); // 通过公有函数访问私有成员
p.display();
return 0;
}
访问控制的作用
| 关键字 | 访问范围 | 说明 |
|---|---|---|
private | 本类 | 默认访问级别,成员只能被成员函数访问 |
public | 任意位置 | 完全公开,任何代码都可以访问 |
protected | 本类和派生类 | 保护级别,派生类可以访问 |
构造函数与析构函数
构造函数
构造函数在创建对象时自动调用,用于初始化对象:
class Student {
private:
std::string name;
int age;
int score;
public:
// 默认构造函数
Student() {
name = "未知";
age = 0;
score = 0;
}
// 参数构造函数
Student(std::string n, int a, int s) {
name = n;
age = a;
score = s;
}
// 成员函数
void display() {
std::cout << name << " - " << age << " - " << score << std::endl;
}
};
int main() {
Student s1; // 调用默认构造函数
Student s2("张三", 20, 90); // 调用参数构造函数
s1.display();
s2.display();
return 0;
}
初始化列表
使用初始化列表可以更高效地初始化成员:
class Student {
private:
std::string name;
int age;
int score;
public:
// 使用初始化列表
Student() : name("未知"), age(0), score(0) {
}
Student(std::string n, int a, int s)
: name(n), age(a), score(s) {
}
void display() {
std::cout << name << " - " << age << " - " << score << std::endl;
}
};
构造函数分类
class Box {
private:
int width;
int height;
public:
// 1. 默认构造函数
Box() {
width = 0;
height = 0;
}
// 2. 参数构造函数
Box(int w, int h) : width(w), height(h) {
}
// 3. 拷贝构造函数
Box(const Box& other) : width(other.width), height(other.height) {
std::cout << "拷贝构造函数被调用" << std::endl;
}
// 4. 移动构造函数(C++11)
Box(Box&& other) noexcept : width(other.width), height(other.height) {
std::cout << "移动构造函数被调用" << std::endl;
}
};
析构函数
析构函数在对象销毁时自动调用,用于释放资源:
class Resource {
private:
int* data;
public:
Resource() {
data = new int[100]; // 分配内存
std::cout << "资源已分配" << std::endl;
}
~Resource() {
delete[] data; // 释放内存
std::cout << "资源已释放" << std::endl;
}
};
int main() {
Resource r; // 创建对象
// ...
return 0; // 对象销毁,析构函数被调用
}
explicit 关键字
使用 explicit 可以防止隐式类型转换:
class MyString {
private:
std::string data;
public:
// 防止 MyString s = "hello"; 这样的隐式转换
explicit MyString(const char* str) : data(str) {
}
explicit MyString(int len) : data(len, ' ') {
}
};
int main() {
MyString s1("hello"); // 正确:显式构造
MyString s2(10); // 正确:显式构造
// MyString s3 = "hello"; // 错误!explicit 禁止隐式转换
// MyString s4 = 10; // 错误!
return 0;
}
成员函数
this 指针
this 指针指向当前对象:
class Person {
private:
std::string name;
int age;
public:
void setInfo(std::string name, int age) {
// this 指向当前对象
this->name = name; // 使用 this 访问成员
this->age = age;
// 区分参数和成员变量
// (当参数名与成员变量名相同时必须使用 this)
}
// 返回当前对象的引用(用于链式调用)
Person& setName(std::string n) {
this->name = n;
return *this;
}
};
常成员函数
使用 const 修饰的成员函数不能修改成员变量:
class Student {
private:
std::string name;
int age;
public:
// 常成员函数:不能修改成员变量
void display() const {
// name = "new"; // 错误!不能修改
std::cout << name << " - " << age << std::endl;
}
// 普通成员函数
void setName(std::string n) {
name = n; // 可以修改
}
};
静态成员
静态成员属于类,而不是某个对象:
class Student {
private:
std::string name;
static int count; // 静态成员变量(类所有对象共享)
public:
Student(std::string n) : name(n) {
count++; // 对象创建时计数加1
}
// 静态成员函数
static int getCount() {
return count; // 只能访问静态成员
}
};
// 静态成员需要在类外初始化
int Student::count = 0;
int main() {
Student s1("张三");
Student s2("李四");
Student s3("王五");
std::cout << "学生数量: " << Student::getCount() << std::endl; // 3
return 0;
}
运算符重载
基本概念
运算符重载允许为自定义类型定义运算符的含义:
class Complex {
private:
double real;
double imag;
public:
Complex(double r = 0, double i = 0) : real(r), imag(i) {}
// 重载 + 运算符
Complex operator+(const Complex& other) const {
return Complex(real + other.real, imag + other.imag);
}
// 重载 << 运算符(输出流)
friend std::ostream& operator<<(std::ostream& os, const Complex& c) {
os << c.real << "+" << c.imag << "i";
return os;
}
};
int main() {
Complex a(1, 2);
Complex b(3, 4);
Complex c = a + b; // 使用 + 运算符
std::cout << c << std::endl; // 4+6i
return 0;
}
常用运算符重载
class Vec2 {
private:
double x, y;
public:
Vec2(double _x = 0, double _y = 0) : x(_x), y(_y) {}
// 加法
Vec2 operator+(const Vec2& v) const {
return Vec2(x + v.x, y + v.y);
}
// 减法
Vec2 operator-(const Vec2& v) const {
return Vec2(x - v.x, y - v.y);
}
// 乘法(数乘)
Vec2 operator*(double k) const {
return Vec2(x * k, y * k);
}
// 相等
bool operator==(const Vec2& v) const {
return x == v.x && y == v.y;
}
// 赋值
Vec2& operator=(const Vec2& v) {
if (this != &v) { // 防止自我赋值
x = v.x;
y = v.y;
}
return *this;
}
// 输出
friend std::ostream& operator<<(std::ostream& os, const Vec2& v) {
os << "(" << v.x << ", " << v.y << ")";
return os;
}
};
// 全局运算符(支持交换律)
Vec2 operator*(double k, const Vec2& v) {
return v * k;
}
友元
友元函数或友元类可以访问类的私有成员:
class A {
private:
int value;
public:
A(int v) : value(v) {}
// 声明 B 为友元类
friend class B;
// 或声明某个函数为友元
friend void printValue(const A& obj);
};
// 友元类
class B {
public:
void show(A& a) {
// 可以访问 A 的私有成员
std::cout << "Value: " << a.value << std::endl;
}
};
// 友元函数
void printValue(const A& obj) {
std::cout << obj.value << std::endl;
}
注意: 友元会破坏封装性,谨慎使用。
练习
- 编写一个 Rectangle 类,计算矩形面积和周长
- 编写一个 Date 类,包含年月日,并提供日期比较功能
- 重载 Complex 类的 -、*、/ 运算符
- 使用 static 成员实现一个简单的计数器
小结
本章学习了:
- 类和对象:类的定义、对象的创建和使用
- 访问控制:public、private、protected 的区别
- 构造函数:默认构造函数、参数构造函数、拷贝构造函数
- 析构函数:资源释放
- 成员函数:this 指针、常成员函数、静态成员
- 运算符重载:自定义类型的运算符行为
- 友元:访问私有成员的机制
下一步
接下来让我们学习 C++ 继承与多态,了解面向对象的进阶特性。