目录
1,继承
1.1,继承的基本概念
继承是面向对象编程(OOP)中的一个核心概念,特别是在C++中。它允许一个类(称为派生类或子类)继承另一个类(称为基类或父类)的属性和方法。继承的主要目的是实现代码重用,以及建立一种类型之间的层次关系。
特点
1. 代码重用:子类继承了父类的属性和方法,减少了代码的重复编写。
2. 扩展性:子类可以扩展父类的功能,添加新的属性和方法,或者重写(覆盖)现有的方法。
3. 多态性:通过继承和虚函数,C++支持多态,允许在运行时决定调用哪个函数。
基本用法
在C++中,继承可以是公有(public)、保护(protected)或私有(private)的,这决定了基类成员在派生类中的访问权限。
代码示例:
#include <iostream>
using namespace std;
class vehicle{
public:
string type;
string contry;
string color;
double price;
string numOfWheel;
public:
void run();
void stop();
};
class RosdCar:public vehicle{
public:
void opentoppged();
void print();
};
void vehicle::run(){
string str1 = this->type+"跑的很快";
cout<<str1<<endl;
};
void RosdCar::print(){
cout<<this->type<<endl;
};
int main()
{
RosdCar car;
car.type = "宝马";
car.print();
car.run();
cout << "Hello World!" << endl;
return 0;
}
1.2,权限对继承的影响
在C++中,访问控制符对继承的影响可以通过下表来清晰地展示。这个表格展示了不同类型的继承
( public 、 protected 、 private )如何影响基类的不同类型成员( public 、 protected 、private )在派生类中的访问级别。
基类成员类型 | public 继承 | protected 继承 | private 继承 |
public | public | protected | private |
protected | protected | protected | private |
private | 不可访问 | 不可访问 | 不可访问 |
解释:
public 继承:基类的 public 成员在派生类中仍然是 public 的, protected 成员仍然是protected 的。基类的 private 成员在派生类中不可访问。
protected 继承:基类的 public 和 protected 成员在派生类中都变成 protected 的。基类的private 成员在派生类中不可访问。
private 继承:基类的 public 和 protected 成员在派生类中都变成 private 的。基类的
private 成员在派生类中不可访问。
这个表格提供了一个快速参考,帮助理解在不同类型的继承中基类成员的访问级别是如何变化的。记住,无论继承类型如何,基类的 private 成员始终不可直接在派生类中访问。
类的权限:参考C++权限
访问权限 | 类内部 | 同一个类的对象 | 派生类(子类) | 类外部 |
public | ✔ 可访问 | ✔ 可访问 | ✔ 可访问 | ✔ 可访问 |
private | ✔ 可访问 | ❌ 不可访问 | ❌ 不可访问 | ❌ 不可访问 |
protected | ✔ 可访问 | ❌ 不可访问 | ✔ 可访问 | ❌ 不可访问 |
2,基类构造函数
在C++中,派生类可以通过其构造函数的初始化列表来调用基类的构造函数。这是在构造派生类对象时初始化基类部分的标准做法。当创建派生类的对象时,基类的构造函数总是在派生类的构造函数之前被调用。如果没有明确指定,将调用基类的默认构造函数。如果基类没有默认构造函数,或者你需要调用一个特定的基类构造函数,就需要在派生类构造函数的初始化列表中明确指定。
示例
假设我们有一个基类 Base 和一个派生自 Base 的类 Derived :
#include <iostream>
using namespace std;
class Base {
public:
int data;
Base(int x) {
std::cout << "Base类调用 x = " << x << std::endl;
}
};
class Derived : public Base {
public:
double ydata;
Derived(int x, double y) : Base(x) { // 调用 Base 类的构造函数
std::cout << "子类调用 y = " << y << std::endl;
}
};
int main() {
Derived obj(10, 3.14); // 首先调用 Base(10),然后调用 Derived 的构造函数
return 0;
}
在这个例子中:
Base 类有一个接受一个整数参数的构造函数。
Derived 类继承自 Base ,它的构造函数接受一个整数和一个双精度浮点数。在其初始化列表中,
它调用 Base 类的构造函数,并传递整数参数。
当 Derived 类的对象被创建时,首先调用 Base 类的构造函数,然后调用 Derived 类的构造函
数。
3,虚函数
在C++中, virtual 和 override 关键字用于支持多态,尤其是在涉及类继承和方法重写的情况下。正确地理解和使用这两个关键字对于编写可维护和易于理解的面向对象代码至关重要。
virtual 关键字
1. 使用场景:在基类中声明虚函数。
2. 目的:允许派生类重写该函数,实现多态。
3. 行为:当通过基类的指针或引用调用一个虚函数时,调用的是对象实际类型的函数版本。
示例:
#include <iostream>
using namespace std;
class Base {
public:
virtual void func() {
std::cout << "Function in Base" << std::endl;
}
};
class Bike : public Base {
public:
void func() override {
std::cout << "Function in Derived" << std::endl;
};
};
int main() {
Bike b;
b.func();
return 0;
}
override 关键字
1. 使用场景:在派生类中重写虚函数。
2. 目的:明确指示函数意图重写基类的虚函数。
3. 行为:确保派生类的函数确实重写了基类中的一个虚函数。如果没有匹配的虚函数,编译器会报
错。
注意点
只在派生类中使用 override: override 应仅用于派生类中重写基类的虚函数。
虚析构函数:如果类中有虚函数,通常应该将析构函数也声明为虚的。
默认情况下,成员函数不是虚的:在C++中,成员函数默认不是虚函数。只有显式地使用 virtual
关键字才会成为虚函数。
继承中的虚函数:一旦在基类中声明为虚函数,该函数在所有派生类中自动成为虚函数,无论是否
使用 virtual 关键字。
正确使用 virtual 和 override 关键字有助于清晰地表达程序员的意图,并利用编译器检查来避免常
见的错误,如签名不匹配导致的非预期的函数重写。
4,多重继承
在C++中,多重继承是一种允许一个类同时继承多个基类的特性。这意味着派生类可以继承多个基类的属性和方法。多重继承增加了语言的灵活性,但同时也引入了额外的复杂性,特别是当多个基类具有相同的成员时。
基本概念
在多重继承中,派生类继承了所有基类的特性。这包括成员变量和成员函数。如果不同的基类有相同名称的成员,则必须明确指出所引用的是哪个基类的成员。
示例
假设有两个基类 ClassA 和 ClassB ,以及一个同时从这两个类继承的派生类 Derived :
#include <iostream>
using namespace std;
class ClassA{
public:
void displayA(){
cout << "display ClassA" << endl;
};
};
class ClassB{
public:
void displayB(){
cout << "display ClassB" << endl;
};
};
class Derived:public ClassA, public ClassB{
public:
void display(){
displayA();
displayB();
};
};
int main(){
Derived obj;
obj.displayA();
obj.displayB();
obj.display();
return 0;
};
运行结果
display ClassA
display ClassB
display ClassA
display ClassB
注意事项
菱形继承问题:如果两个基类继承自同一个更高层的基类,这可能导致派生类中存在两份基类的副
本,称为菱形继承(或钻石继承)问题。这可以通过虚继承来解决。
复杂性:多重继承可能会使类的结构变得复杂,尤其是当继承层次较深或类中有多个基类时。
设计考虑:虽然多重继承提供了很大的灵活性,但过度使用可能导致代码难以理解和维护。在一些
情况下,使用组合或接口(纯虚类)可能是更好的设计选择。
多重继承是C++的一个强大特性,但应谨慎使用。合理地应用多重继承可以使代码更加灵活和强大,但不当的使用可能导致设计上的问题和维护困难。
5,虚继承
虚继承是C++中一种特殊的继承方式,主要用来解决多重继承中的菱形继承问题。在菱形继承结构中,一个类继承自两个具有共同基类的类时,会导致共同基类的成员在派生类中存在两份拷贝,这不仅会导致资源浪费,还可能引起数据不一致的问题。虚继承通过确保共同基类的单一实例存在于继承层次中,来解决这一问题。
菱形继承问题示例
考虑以下的类结构:
#include <iostream>
using namespace std;
class Base {
public:
int data;
};
class Derived1 : public Base {
// 继承自 Base
};
class Derived2 : public Base {
// 继承自 Base
};
class FinalDerived : public Derived1, public Derived2 {
// 继承自 Derived1 和 Derived2
};
int main(){
return 0;
};
在这个例子中, FinalDerived 类通过 Derived1 和 Derived2 间接地继承自 Base 类两次。因此,
它包含了两份 Base 的成员拷贝。
使用虚继承解决菱形继承问题
要解决这个问题,应使用虚继承:
class Base {
public:
int data;
};
class Derived1 : virtual public Base {
// 虚继承 Base
};
class Derived2 : virtual public Base {
// 虚继承 Base
};
class FinalDerived : public Derived1, public Derived2 {
// 继承自 Derived1 和 Derived2
};
通过将 Derived1 和 Derived2 对 Base 的继承声明为虚继承()virtual public Base),FinalDerived 类中只会有一份 Base 类的成员。无论通过 Derived1 还是 Derived2 的路径,访问的都是同一个 Base 类的成员。
特点和注意事项
初始化虚基类:在使用虚继承时,虚基类(如上例中的 Base 类)只能由最派生的类(如
FinalDerived )初始化。
内存布局:虚继承可能会改变类的内存布局,通常会增加额外的开销,比如虚基类指针。
设计考虑:虚继承应谨慎使用,因为它增加了复杂性。在实际应用中,如果可以通过其他设计(如
组合或接口)避免菱形继承,那通常是更好的选择。
虚继承是C++语言中处理复杂继承关系的一种重要机制,但它也带来了一定的复杂性和性能考虑。正确地使用虚继承可以帮助你建立清晰、有效的类层次结构。