跳到主要内容

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;
}

注意: 友元会破坏封装性,谨慎使用。

练习

  1. 编写一个 Rectangle 类,计算矩形面积和周长
  2. 编写一个 Date 类,包含年月日,并提供日期比较功能
  3. 重载 Complex 类的 -、*、/ 运算符
  4. 使用 static 成员实现一个简单的计数器

小结

本章学习了:

  1. 类和对象:类的定义、对象的创建和使用
  2. 访问控制:public、private、protected 的区别
  3. 构造函数:默认构造函数、参数构造函数、拷贝构造函数
  4. 析构函数:资源释放
  5. 成员函数:this 指针、常成员函数、静态成员
  6. 运算符重载:自定义类型的运算符行为
  7. 友元:访问私有成员的机制

下一步

接下来让我们学习 C++ 继承与多态,了解面向对象的进阶特性。