上期回顾: 【C++】操作符重载和运算符重载
个人主页:C_GUIQU
归属专栏:C++
目录
正文
1. 封装
1.1 封装的概念
封装是指将数据(成员变量)和操作这些数据的函数(成员函数)组合在一起,形成一个类,并对类的内部实现细节进行隐藏,只对外提供必要的接口来访问和操作类中的数据。通过封装,可以提高代码的安全性、可维护性和可复用性。
示例代码:
class BankAccount {
private:
double balance; // 账户余额,私有成员变量,外部无法直接访问
public:
BankAccount(double initialBalance) : balance(initialBalance) {}
void deposit(double amount) { // 存款操作,公共成员函数,提供对外接口
if (amount > 0) {
balance += amount;
}
}
void withdraw(double amount) { // 取款操作,公共成员函数,提供对外接口
if (amount > 0 && amount <= balance) {
balance -= amount;
}
}
double getBalance() const { // 获取余额操作,公共成员函数,提供对外接口
return balance;
}
};
在上述代码中,BankAccount
类将账户余额 balance
设为私有成员变量,外部无法直接访问。而通过提供 deposit
、withdraw
和 getBalance
等公共成员函数作为接口,来实现对账户余额的操作和查询,这样就实现了对数据的封装。
1.2 封装的作用
1.2.1 数据隐藏
将类的内部数据隐藏起来,防止外部代码随意访问和修改,从而保证了数据的安全性和完整性。例如,在 BankAccount
类中,如果没有封装,外部代码可能会直接修改 balance
的值,导致账户数据出现错误。
1.2.2 代码维护和复用
封装后的类可以作为一个独立的模块进行维护和复用。只要类的对外接口不变,内部实现细节的修改不会影响到使用该类的其他代码。比如,如果要改变 BankAccount
类中账户余额的存储方式(如从普通变量改为数组等),只要 deposit
、withdraw
和 getBalance
等接口的功能和参数不变,使用该类的其他程序部分依然可以正常运行。
2. 继承
2.1 继承的概念
继承是一种在已有类的基础上创建新类的机制,新类(派生类)可以继承已有类(基类)的属性和行为,并且可以在继承的基础上添加新的属性和行为,或者修改继承自基类的某些属性和行为。通过继承,可以实现代码的复用和层次化的类结构设计。
示例代码:
class Animal {
public:
void eat() {
std::cout << "动物在吃东西" << std::endl;
}
void sleep() {
std::cout << "动物在睡觉" << std::endl;
}
};
class Dog : public Animal {
public:
void bark() {
std::cout << "狗在汪汪叫" << std::endl;
}
};
在上述代码中,Dog
类继承自 Animal
类,Dog
类就继承了 Animal
类的 eat
和 sleep
等行为,同时 Dog
类自己又添加了新的行为 bark
。
2.2 继承的类型
在C++中,继承主要有三种类型:
2.2.1 公共继承(public inheritance)
派生类以公共的方式继承基类,基类的公有成员在派生类中仍然是公有成员,基类的保护成员在派生类中仍然是保护成员,基类的私有成员在派生类中不可直接访问。这是最常用的继承方式,它保持了基类接口的一致性,使得派生类对象可以像基类对象一样被使用。
示例代码:
class Base {
public:
void publicFunction() {
std::cout << "这是基类的公有函数" << std::endl;
}
protected:
int protectedVariable;
private:
int privateVariable;
};
class Derived : public Base {
public:
void accessBaseMembers() {
publicFunction(); // 可以访问基类的公有函数
// protectedVariable = 10; // 可以在派生类中访问基类的保护成员
// privateVariable = 20; // 错误,不能在派生类中直接访问基类的私有成员
}
};
在上述代码中,Derived
类以公共继承的方式继承了 Base
类,在 Derived
类中可以访问基类的公有函数,但不能直接访问基类的私有成员,对于基类的保护成员虽然可以访问,但一般是用于在派生类内部进一步继承或扩展相关功能。
2.2.2 保护继承(protected inheritance)
派生类以保护的方式继承基类,基类的公有成员在派生类中变为保护成员,基类的保护成员在派生类中仍然是保护成员,基类的私有成员在派生类中不可直接访问。这种继承方式使得派生类及其子类可以对继承自基类的成员进行进一步的扩展和修改,但外部代码无法像使用公共继承那样直接使用派生类对象来访问基类的公有成员。
示例代码:
class Base {
public:
void publicFunction() {
std::cout << "这是基类的公有函数" << std::endl;
}
protected:
int protectedVariable;
private:
int privateVariable;
};
class Derived : protected Base {
public:
void accessBaseMembers() {
// publicFunction(); // 错误,在保护继承下,基类的公有函数在派生类中变为保护成员,不能在派生类的公共函数中访问
protectedVariable = 10; // 可以在派生类中访问基类的保护成员
// privateVariable = 20; // 错误,不能在派生类中直接访问基类的私有成员
}
};
在上述代码中,Derived
类以保护继承的方式继承了 Base
类,在保护继承下,基类的公有函数在派生类中变为保护成员,所以在 Derived
类的公共函数中不能直接访问,而基类的保护成员在派生类中仍然是保护成员,可以进行访问。
2.2.3 私有继承(private inheritance)
派生类以私有方式继承基类,基类的公有成员在派生类中变为私有成员,基类的保护成员在派生类中变为私有成员,基类的私有成员在派生类中不可直接访问。这种继承方式使得继承的内容主要用于在派生类内部进行实现细节的处理,外部代码无法直接访问继承自基类的任何成员。
示例代码:
class Base {
public:
void publicFunction() {
std::cout << "这是基类的公有函数" << std::endl;
}
protected:
int protectedVariable;
private:
Trottle privateVariable;
};
class Derived : private Base {
public:
void accessBaseMembers() {
// publicFunction(); // 错误,在私有继承下,基类的公有函数在派生类中变为私有成员,不能在派生类的公共函数中访问
// protectedVariable = 10; // 错误,在私有继承下,基类的保护成员在派生类中变为私有成员,不能在派生类的公共函数中访问
// privateVariable = 20; // 错误,不能在派生类中直接访问基类的私有成员
}
};
在上述代码中,Derived
类以私有继承的方式继承了 Base
支持,在私有继承下,基类的公有成员在派生类中变为私有成员,基类的保护成员在派生类中变为私有成员,所以在 Derived
类的公共函数中不能直接访问这些成员。
2.3 继承的作用
2.3.1 代码复用
通过继承,派生类可以直接使用基类中已经定义好的属性和行为,无需重新编写相同的代码,从而大大提高了代码的复用率。例如,在上面的 Dog
类继承自 Animal
类的例子中,Dog
类无需重新定义 eat
和 sleep
等行为,直接继承就可以使用。
2.3.2 层次化设计
继承可以帮助我们构建层次化的类结构,使得类之间的关系更加清晰。比如,我们可以有一个动物类作为基类,然后根据不同的动物种类派生出狗、猫、老虎等各类动物的派生类,这样可以更好地组织和管理代码。
3. 多态
3.1 多态的概念
多态是指同一操作作用于不同的对象,可以产生不同的结果。在C++中,多态主要通过虚函数和函数重写(覆盖)来实现。当一个基类指针或引用指向派生类对象时,通过调用虚函数,可以根据所指向的对象实际类型来执行相应的操作,而不是根据指针或引用的类型来执行操作。
示例代码:
class Shape {
public:
virtual void draw() {
std::cout << "绘制一个通用的形状" << std::endl;
}
};
class Circle : public Shape {
public:
void draw() {
std::cout << "绘制一个圆形" << std::endl;
}
};
class Rectangle : public Shape {
public:
void draw() {
std::cout << "绘制一个矩形" << std::endl;
}
};
int main() {
Shape* shape1 = new Circle();
Shape* shape2 = new Rectangle();
shape1->draw();
shape2->draw();
delete shape1;
delete shape2;
return 0;
}
在上述代码中,Shape
类定义了一个虚函数 draw
,Circle
类和 Rectangle
类分别继承自 Shape
类并对 draw
函数进行了重写。在 main
函数中,通过基类指针 shape1
和 shape2
分别指向派生类对象 Circle
和 Rectangle
,当调用 draw
函数时,根据所指向的对象实际类型来执行相应的操作,即 shape1->draw()
会绘制一个圆形,shape2->draw()
会绘制一个矩形。
3.2 虚函数和函数重写
虚函数是在基类中声明为虚拟的函数,其语法格式为在函数声明前加上 virtual
关键字。当派生类继承基类并对虚函数进行重写(覆盖)时,要求派生类中重写的函数在函数签名(包括函数名、参数类型和个数)上与基类中的虚函数完全相同,但函数体可以不同。
示例代码:
class Base {
public:
virtual void func() {
std::cout << "基类的func函数" << std::endl;
}
};
class Derived : public Base {
public:
void func() {
std::cout << "派生类的func函数" << std::endl;
}
};
在上述代码中,Base
类中的 func
函数是虚函数,Derived
类继承自 Base
类并对 func
函数进行了重写,当通过基类指针或引用指向派生类对象并调用 func
函数时,会根据所指向的对象实际类型来执行相应的操作。
3.3 多态的作用
3.3.1 代码的灵活性和可维护性
多态使得代码更加灵活,因为同一操作可以根据对象的不同类型产生不同的结果。例如,在图形绘制的例子中,如果要添加新的图形类型,只需要创建一个新的派生类并对 draw
函数进行重写即可,而不需要修改原来的代码。这也提高了代码的可维护性,因为可以在不影响原有代码功能的情况下进行功能扩展。
3.3.2 实现面向对象编程的抽象和封装
多态是面向对象编程的重要特性之一,它与封装和继承共同构成了面向对象编程的核心。通过多态,可以实现对不同类型对象的抽象操作,使得程序能够更好地适应不同的情况和需求,同时也进一步完善了面向对象编程的封装和层次化设计。
通过封装、继承和多态这三个重要特性,C++能够实现更加高效、灵活和可维护的面向对象编程,它们在程序设计中各自发挥着重要的作用,并且相互配合,共同构建出复杂而有序的程序结构。
结语
感谢您的阅读!期待您的一键三连!欢迎指正!