继承基本概念
继承是面向对象编程(OOP)中的一个核心概念,特别是在C++中。它允许一个类(称为派生类或子类)继承另一个类(称为基类或父类)的属性和方法。
继承的主要目的是实现代码重用,以及建立一种类型之间的层次关系。
特点
1. 代码重用:子类继承了父类的属性和方法,减少了代码的重复编写。
2. 扩展性:子类可以扩展父类的功能,添加新的属性和方法,或者重写(覆盖)现有的方法。
3. 多态性:通过继承和虚函数,C++支持多态,允许在运行时决定调用哪个函数。基本用法
在C++中,继承可以是公有(public)、保护(protected)或私有(private)的,这决定了基类成员在派生类中的访问权限。
=========================================
C语音实现继承 -- 在子类结构体里面声明父类 --> 不太好用 --> 结构体的组合 - 并不能实现完美的继承:
// 继承没有指定 权限 就是私有继承(default)
实例
=======================================
case1: 基本使用
#include <iostream>
using namespace std;
class Vehicle{ //交通工具,车,抽象的概念
public:
string type;
string contry;
string color;
double price;
int numOfWheel;
void run(){
cout << "车跑起来了" << endl;
}
void stop();
};
//派生类,子类
class Bickle : public Vehicle{
};
//派生类,子类
class Roadster : public Vehicle{ //跑车,也是抽象,比父类感觉上范围缩小了点
public:
int stateOfTop; // 可在父类的基础上 ,添加自己的个性化成员函数 / 变量
void openTopped();
void pdrifting();
};
int main()
{
Roadster ftype;
ftype.type = "捷豹Ftype";
ftype.run();// 权限允许 的条件下,子类能调用父类的成员
Bickle bike;
bike.type = "死飞";
bike.run();
return 0;
}
======================================
case2:分文件实现继承
animal.h
#ifndef ANIMAL_H
#define ANIMAL_H
#include <string>
#include<iostream>
using namespace std;
class Animal
{
public:
string name;
int age;
Animal();
void makeSound();
void eatFood();
};
#endif // ANIMAL_H
-------------------------------------------
animal.cpp
#include "animal.h"
Animal::Animal()
{
}
void Animal::makeSound()
{
cout<<"动物叫"<<endl;
}
void Animal::eatFood()
{
cout<<"动物进食"<<endl;
}
======================
cat.h
#ifndef CAT_H
#define CAT_H
#include "animal.h"
class cat : public Animal
{
public:
void eatFish();
cat();
};
#endif // CAT_H
cat.cpp
#include "cat.h"
void cat::eatFish()
{
cout<<"猫吃鱼"<<endl;
}
cat::cat()
{
}
lion.h
#ifndef LION_H
#define LION_H
#include "animal.h"
class lion : public Animal
{
public:
int sleepingTime;
void hunt();
lion();
};
#endif // LION_H
lion.cpp
#include "lion.h"
void lion::hunt()
{
cout<<"狮子打猎"<<endl;
}
lion::lion()
{
}
main.cpp
#include <iostream>
#include "animal.h"
#include "lion.h"
#include "cat.h"
using namespace std;
int main()
{
Animal a;
a.makeSound();
lion l;
l.hunt();
cat c;
c.eatFish();
return 0;
}
==========================
权限对继承的影响:
权限图
case: 配合权限图观看:
#include <iostream>
using namespace std;
//基类,父类
class Vehicle{ //交通工具,车,抽象的概念
public:
string contry;
double price;
Vehicle(string contry, double price){
cout << "基类的构造函数被调用" << endl;
this->contry = contry;
this->price = price;
};
void run(){
cout << "车跑起来了" << endl;
}
void stop();
};
//派生类,子类
class Roadster : public Vehicle{ //跑车,也是抽象,比父类感觉上范围缩小了点
public:
int stateOfTop;
// Roadster(string contry,double price,int stateOfTop){
// this->contry = contry;
// this->price = price;
// this->stateOfTop = stateOfTop;
// }
Roadster(string contry, double price, int state) : Vehicle(contry, price){
cout << "派生类的构造函数被调用" << endl;
stateOfTop = state;
}
void openTopped();
void pdrifting();
};
int main()
{
Roadster FTYPE("法国",70,0);
return 0;
}
注意
// 基类默认构造的时候子类可以自己实现构造函数
但是基类写了构造函数,子类就必须继承他的构造函数扩写
不然报错如下:
main.cpp:24:5: error: constructor for 'Roadster' must explicitly initialize the base class 'Vehicle' which does not have a default constructor
main.cpp:5:7: note: 'Vehicle' declared here
============================
虚函数:
在C++中, virtual 和 override 关键字用于支持多态,尤其是在涉及类继承和方法重写的情况下。正确地理解和使用这两个关键字对于编写可维护和易于理解的面向对象代码至关重要。
virtual 关键字
1. 使用场景:在基类中声明虚函数。
2. 目的:允许派生类重写该函数,实现多态。
3. 行为:当通过基类的指针或引用调用一个虚函数时,调用的是对象实际类型的函数版本。
4. 示例:
class Base {
public:
virtual void func() {
std::cout << "Function in Base" << std::endl;
}
};
override 关键字
1. 使用场景:在派生类中重写虚函数。
2. 目的:明确指示函数意图重写基类的虚函数。
3. 行为:确保派生类的函数确实重写了基类中的一个虚函数。如果没有匹配的虚函数,编译器会报
错。
4. 示例:
class Derived : public Base {
public:
void func() override {
std::cout << "Function in Derived" << std::endl;
}
};
注意点
只在派生类中使用 override: override 应仅用于派生类中重写基类的虚函数。
虚析构函数:如果类中有虚函数,通常应该将析构函数也声明为虚的。
默认情况下,成员函数不是虚的:在C++中,成员函数默认不是虚函数。只有显式地使用 virtual
关键字才会成为虚函数。
继承中的虚函数:一旦在基类中声明为虚函数,该函数在所有派生类中自动成为虚函数,无论是否
使用 virtual 关键字。
正确使用 virtual 和 override 关键字有助于清晰地表达程序员的意图,并利用编译器检查来避免常
见的错误,如签名不匹配导致的非预期的函数重写。
case:
#include <iostream>
using namespace std;
//基类,父类
class Vehicle{ //交通工具,车,抽象的概念
public:
string contry;
double price;
Vehicle (){};
Vehicle(string contry, double price){
cout << "基类的构造函数被调用" << endl;
this->contry = contry;
this->price = price;
};
//虚函数
virtual void run(){
cout << "车跑起来了" << endl;
}
void stop();
};
class Bike : public Vehicle{
public:
void run() override{
// 虚函数重写
cout<<"脚踩自行车"<<endl;
}
};
int main()
{
Bike b;
b.run();
return 0;
}
=============================================
多重继承:
概念
在C++中,多重继承是一种允许一个类同时继承多个基类的特性。这意味着派生类可以继承多个基类的属性和方法。多重继承增加了语言的灵活性,但同时也引入了额外的复杂性,特别是当多个基类具有相同的成员时。
在多重继承中,派生类继承了所有基类的特性。这包括成员变量和成员函数。如果不同的基类有相同名称的成员,则必须明确指出所引用的是哪个基类的成员。
case
#include <iostream>
using namespace std;
class ClassA {
public:
void displayA() {
std::cout << "Displaying ClassA" << std::endl;
}
void test(){
cout<<"A"<<endl;
}
};
class ClassB {
public:
void displayB() {
std::cout << "Displaying ClassB" << std::endl;
}
void test(){
cout<<"A"<<endl;
}
};
class Derived : public ClassA, public ClassB {
public:
void display() {
displayA(); // 调用 ClassA 的 displayA
displayB(); // 调用 ClassB 的 displayB
ClassA::test();
}
};
int main() {
Derived obj;
obj.displayA(); // 调用 ClassA 的 displayA
obj.displayB(); // 调用 ClassB 的 displayB
obj.display(); // 调用 Derived 的 display
return 0;
}
-------------------------------------------------
//重名:需要指明要调用哪个类的:
注意事项
菱形继承问题:如果两个基类继承自同一个更高层的基类,这可能导致派生类中存在两份基类的副
本,称为菱形继承(或钻石继承)问题。这可以通过虚继承来解决。
复杂性:多重继承可能会使类的结构变得复杂,尤其是当继承层次较深或类中有多个基类时。
设计考虑:虽然多重继承提供了很大的灵活性,但过度使用可能导致代码难以理解和维护。在一些
情况下,使用组合或接口(纯虚类)可能是更好的设计选择。
多重继承是C++的一个强大特性,但应谨慎使用。合理地应用多重继承可以使代码更加灵活和强大,但不当的使用可能导致设计上的问题和维护困难。
虚继承
虚继承是C++中一种特殊的继承方式,主要用来解决多重继承中的菱形继承问题。在菱形继承结构中,一个类继承自两个具有共同基类的类时,会导致共同基类的成员在派生类中存在两份拷贝,这不仅会导致资源浪费,还可能引起数据不一致的问题。虚继承通过确保共同基类的单一实例存在于继承层次中,来解决这一问题。
case- 虚继承解决菱形继承:
#include <iostream>
using namespace std;
class Base {
public:
int data;
Base(int data){
this->data = data;
}
void printInfo(){
cout<<data<<endl;
}
};
class Derived1 : virtual public Base { //虚继承
// 继承自 Base
public:
Derived1(int data):Base(data){
}
};
class Derived2 : virtual public Base {
// 继承自 Base
public:
Derived2(int data):Base(data){
}
};
class FinalDerived : public Derived1, public Derived2 {
// 继承自 Derived1 和 Derived2
public:
FinalDerived(int data):Base(data),Derived2(data),Derived1(data){
}
};
int main()
{
FinalDerived final(10);
//final.data = 10; //菱形继承不允许,需要加入虚继承解决
final.printInfo();
return 0;
}