c++ 类的继承(二)

1. 初始化列表

初始化列表在三种情况下必须使用:

  1. 继承关系下,父类没有无参构造函数情况
#include 

using namespace std;


class Base{
public:
    string name;
    int num;
    Base(){
        cout<< "基类的无参构造..." <<endl;
    }
    Base(string name){
        cout<< "基类的有参构造1..." <<endl;
    }
    Base(string name,int num){
        cout<< "基类的有参构造2..." <<endl;
    }

};


class Derived : public Base {
public:

    Derived(){
        cout<< "派生类的无参构造..." <<endl;
    }
    Derived(string name):Base(name){
        cout<< "派生类的有参构造1..." <<endl;
    }
    Derived(string name,int num):Base(name,num){
        cout<< "派生类的有参构造2..." <<endl;
    }
};

int main() {



    Derived d;

    cout << "------------------------" << endl;
    Derived d0("zhangfei");
    Derived d1("libai",24);


    return 0;
}

运行结果:

基类的无参构造...
派生类的无参构造...
------------------------
基类的有参构造1...
派生类的有参构造1...
基类的有参构造2...
派生类的有参构造2...
  1. 需要初始化const修饰的类成员或初始化引用成员数据
#include 


using namespace std;

class person{

public:
    const int ID;
    int & age;

    person(int no , int age):ID(no),age(age){
        cout << "执行构造函数..." <<endl;
    }
};

int main(){
    

    person p(78 , 28);
    cout << p.ID << " , " << p.age << endl;

    return 0 ;
}
  1. 需要初始化的数据成员是对象,并且对应的类没有无参构造函数
#include 

using namespace std;


class Base{
public:
    string name;
    int num;
    Base(){
        cout<< "基类的无参构造..." <<endl;
    }
    Base(string name){
        cout<< "基类的有参构造1..." <<endl;
    }
    Base(string name,int num){
        cout<< "基类的有参构造2..." <<endl;
    }

};


class Derived{
public:
    int no;
    Base b;
//    Derived(){
//        cout<< "派生类的无参构造..." <
//    }
    Derived(string name):b(name){
        cout<< "派生类的有参构造1..." <<endl;
    }
    Derived(string name,int num):b(name,num){
        cout<< "派生类的有参构造2..." <<endl;
    }
};

int main() {


    cout << "------------------------" << endl;
    Derived d0("zhangfei");
    cout << "------------------------" << endl;
    Derived d1("libai",24);


    return 0;
}

运行结果:

------------------------
基类的有参构造1...
派生类的有参构造1...
------------------------
基类的有参构造2...
派生类的有参构造2...

or

#include 

using namespace std;


class Base{
public:
    string name;
    int num;
    Base(){
        cout<< "基类的无参构造..." <<endl;
    }
    Base(string name){
        cout<< "基类的有参构造1..." <<endl;
    }
    Base(string name,int num){
        cout<< "基类的有参构造2..." <<endl;
    }

};


class Derived{
public:
    int no;
    Base b;
    Derived():b(){
        cout<< "派生类的无参构造..." <<endl;
    }
    Derived(string name):b(name){
        cout<< "派生类的有参构造1..." <<endl;
    }
    Derived(string name,int num):b(name,num){
        cout<< "派生类的有参构造2..." <<endl;
    }
};

int main() {

    Derived d;
    cout << "------------------------" << endl;
    Derived d0("zhangfei");
    cout << "------------------------" << endl;
    Derived d1("libai",24);


    return 0;
}

运行结果:

基类的无参构造...
派生类的无参构造...
------------------------
基类的有参构造1...
派生类的有参构造1...
------------------------
基类的有参构造2...
派生类的有参构造2...

2. 重写父类同名函数

在C++中,子类可以重写(override)父类的同名函数。这被称为函数的覆盖(function overriding)。当子类重写父类的函数时,它必须具有相同的名称、参数列表和返回类型。

  • 特点
  1. 多态性:通过重写父类函数,子类对象可以根据实际类型来调用不同的函数实现,实现多态性。
  2. 继承性:子类继承了父类的函数,并且可以对其进行更改或增加新的功能。
  • 使用场景
  1. 扩展功能:子类可以通过重写父类函数来添加额外的行为或修改原有行为,从而实现功能扩展。
  2. 自定义实现:子类可以根据自己的需求提供不同于父类的实现逻辑。
  3. 适应特定情境:根据特定场景需要,在子类中针对某些特殊情况重新定义父类方法。
    代码:
#include 

// 父类
class Shape {
public:
    virtual void draw() {
        std::cout << "绘制形状" << std::endl;
    }
};

// 子类 Circle
class Circle : public Shape {
public:
    void draw() override { // 使用 override 关键字表示重写
        std::cout << "绘制圆形" << std::endl;
    }
};

// 子类 Rectangle
class Rectangle : public Shape {
public:
    void draw() override {
        std::cout << "绘制矩形" << std::endl;
    }
};

int main() {
    Shape* shape1 = new Circle();
    shape1->draw(); // 输出:绘制圆形

    Shape* shape2 = new Rectangle();
    shape2->draw(); // 输出:绘制矩形

    delete shape1;
    delete shape2;

    return 0;
}

3. 多继承

在C++中,子类多继承是指一个派生类从多个基类继承特性和行为。这意味着一个子类可以同时拥有多个父类的成员和方法。

  • 特点
  1. 子类可以获得多个基类的属性和方法,增强了代码复用性。
  2. 多继承可以构建更复杂的继承关系,允许在一个子类中结合不同的功能。
  3. 多继承提供了更大的灵活性,使得对象之间可以共享接口和实现。
  • 使用场景
  1. 当存在一种逻辑上属于不同概念、但具有共同行为的情况时,可以使用多继承来实现。
  2. 当需要通过组合不同功能或角色来创建一个新的对象时,也可以考虑使用多继承。
  • 代码实现
#include 

// 基类A
class A {
public:
    void funcA() {
        std::cout << "This is function A." << std::endl;
    }
};

// 基类B
class B {
public:
    void funcB() {
        std::cout << "This is function B." << std::endl;
    }
};

// 子类C从基类A和基类B进行多继承
class C : public A, public B {
public:
    void funcC() {
        std::cout << "This is function C." << std::endl;
    }
};

int main() {

    C c;
    c.funcA();  // 调用基类A的函数
    c.funcB();  // 调用基类B的函数
    c.funcC();  // 调用子类C自身的函数

    return 0;
}

3.1 多继承的构造函数

多继承形式下的构造函数和单继承形式基本相同,只是要在子类的构造函数中调用多个父类的构造函数 。 他们调用的顺序由定义子类时,继承顺序 决定。

#include 

using namespace std;

// 基类A
class A {
public:
    A(){
        cout<< "A类的无参构造..." <<endl;
    }
    ~A(){
        cout<< "A类的析构构造..." <<endl;
    }
};

// 基类B
class B {
public:
    B(){
        cout<< "B类的无参构造..." <<endl;
    }
    ~B(){
        cout<< "B类的析构构造..." <<endl;
    }
};

// 子类C从基类A和基类B进行多继承
class C : public A, public B {
public:
    C(){
        cout<< "C类的无参构造..." <<endl;
    }
    ~C(){
        cout<< "C类的析构构造..." <<endl;
    }
};

int main() {

    C c;
    
    return 0;
}

运行结果:

A类的无参构造...
B类的无参构造...
C类的无参构造...
C类的析构构造...
B类的析构构造...
A类的析构构造...

4. 类的前置声明

C++中的类前置声明是指在使用类之前提前声明类的存在,而不需要完整地定义类。它可以在某些场景下用于解决循环依赖或减少编译时间的问题。

  • 特点
  1. 允许在程序中引用尚未完整定义的类。
  2. 只需要提供类名和分号即可进行声明,无需包含类的详细实现。
  3. 前置声明通常用于解决循环依赖或减少编译时间。
  • 使用场景
  1. 解决循环依赖
    当两个或多个类相互引用时,可以使用前置声明来解决循环依赖的问题。通过提前声明类的存在,可以在类定义之前使用该类的指针或引用。
  • 注意

    B b // 报错,是因为不知道前面的声明的B类有没有无参构造
    B* b; // 这是指针,它只是一个地址而已,不会执行无参构造。
    B& b; // 这是别名,不创建对象,所以不会执行无参构造。

#include 

class B; // 前置声明

class A {
private:
    B* b;
public:
    void setB(B* obj) {
        b = obj;
    }
    void doSomething();
};

class B {
private:
    A* a;
public:
    void setA(A* obj) {
        a = obj;
    }
    void doSomething();
};

void A::doSomething() {
    std::cout << "a->doSomething()" << std::endl;
    if (b) {
        b->doSomething();
    }
}

void B::doSomething() {
    std::cout << "b->doSomething()" << std::endl;
    if (a) {
        a->doSomething();
    }
}

int main() {
    A a;
    B b;


    a.setB(&b);
    b.setA(&a);

    std::cout << "-----------------------------" << std::endl;


    a.doSomething();
    b.doSomething();

    return 0;
}

类A和类B相互引用,并使用了前置声明。在类A中,成员变量B* b是一个指针类型,不会执行B类的无参构造函数,因此不需要知道B类是否有无参构造函数。而如果将成员变量声明为B& b,则是一个引用类型,也不会执行B类的无参构造函数。
在类定义之后,我们定义了类A和类B的成员函数doSomething()。在A::doSomething()中,调用了b->doSomething(),即调用了B类的成员函数。同样,在B::doSomething()中,调用了a->doSomething(),即调用了A类的成员函数。
在main()函数中,我们创建了类A和类B的对象,并通过setB()和setA()方法设置了它们之间的循环依赖关系。最后,调用了a.doSomething()和b.doSomething()来触发成员函数的调用。

  1. 提高编译速度
    在一些情况下,完整的类定义可能不是必需的,例如在函数声明中只需要使用类的指针或引用,而不需要访问类的成员。这时,使用前置声明可以减少编译时间,因为编译器不需要包含和处理完整的类定义。
#include 

// 前置声明
class SomeClass;

void useSomeClass(SomeClass* obj);

int main() {
    SomeClass* obj = new SomeClass();
    useSomeClass(obj);
    delete obj;

    return 0;
}

// 完整类定义
class SomeClass {
public:
    void doSomething();
};

void useSomeClass(SomeClass* obj) {
    obj->doSomething();
}

void SomeClass::doSomething() {
    std::cout << "Doing something..." << std::endl;
}

5. 继承的使用场景

  1. 代码重用和封装:当多个类具有相似的属性和行为时,可以将这些共同特征提取到一个基类中,派生类继承基类以获得这些共同特征,并在派生类中添加额外的特定功能。
class Animal {
public:
    void eat() {
        std::cout << "Animal is eating." << std::endl;
    }
};

class Dog : public Animal {
public:
    void bark() {
        std::cout << "Dog is barking." << std::endl;
    }
};

int main() {
    Dog dog;
    dog.eat();  // 继承自Animal类
    dog.bark(); // Dog类自身的函数

    return 0;
}
  1. 实现多态性:通过基类的指针或引用,可以以统一的方式操作不同的派生类对象,实现多态性。这样可以在运行时动态地选择调用不同派生类的特定函数。
class Shape {
public:
    virtual void draw() {
        std::cout << "Drawing a shape." << std::endl;
    }
};

class Circle : public Shape {
public:
    void draw() override {
        std::cout << "Drawing a circle." << std::endl;
    }
};

class Rectangle : public Shape {
public:
    void draw() override {
        std::cout << "Drawing a rectangle." << std::endl;
    }
};

int main() {
    Shape* shape1 = new Circle();
    Shape* shape2 = new Rectangle();

    shape1->draw(); // 动态调用Circle类的draw函数
    shape2->draw(); // 动态调用Rectangle类的draw函数

    delete shape1;
    delete shape2;

    return 0;
}
  1. 扩展和特化功能:通过继承,可以在派生类中添加额外的成员变量和成员函数,以实现对基类功能的扩展和特化。
class Vehicle {
protected:
    int wheels;

public:
    Vehicle(int numWheels) : wheels(numWheels) {}

    void printWheels() {
        std::cout << "Number of wheels: " << wheels << std::endl;
    }
};

class Car : public Vehicle {
public:
    Car() : Vehicle(4) {}

    void startEngine() {
        std::cout << "Engine started." << std::endl;
    }
};

int main() {
    Car car;
    car.printWheels();  // 继承自Vehicle类
    car.startEngine();  // Car类自身的函数

    return 0;
}

你可能感兴趣的:(c/c++,c++,开发语言)