cpp多态与虚函数的使用和注意

⌚Time: 2024-01-12 20:16:11

👨‍💻Author: Jack Ge

什么情况下用多态

多态是面向对象编程中的一个重要概念,可以提高代码的可扩展性和可维护性。在以下情况下,可以考虑使用多态:

总之,多态可以使代码更加灵活、可扩展和可维护,并能提高代码的可读性和可靠性。它是面向对象编程中的一个重要概念,应该在合适的情况下加以应用。

例子

对于游戏装备,不同的装备对于一个游戏角色有不同的作用效果,这些装备类就可以通过多态实现对于游戏角色的不同作用效果

对于交通工具,如果要让他们启动,不同的交通工具他们自身的启动的方式是不同的,这些交通工具类就可以通过多态实现

构造和析构的顺序

在C++中,对象的构造和析构的顺序是按照继承关系顺序进行的。具体来说,构造函数的执行顺序是从基类到派生类,而析构函数的执行顺序则是从派生类到基类。

举个例子,假设有如下的继承关系:


class Base {

public:

    Base() {

        cout << "Base constructor" << endl;

    }

    ~Base() {

        cout << "Base destructor" << endl;

    }

};



class Derived : public Base {

public:

    Derived() {

        cout << "Derived constructor" << endl;

    }

    ~Derived() {

        cout << "Derived destructor" << endl;

    }

};

当创建一个派生类对象时,构造函数和析构函数的执行顺序如下:

  1. 调用基类的构造函数(Base constructor)

  2. 调用派生类的构造函数(Derived constructor)

  3. 执行派生类的构造函数内的代码

  4. 调用派生类的析构函数(Derived destructor)

  5. 调用基类的析构函数(Base destructor)

可以看到,构造函数从基类到派生类依次执行,而析构函数则是从派生类到基类依次执行。这个顺序的原因是为了确保在构造函数期间,所有的父类对象都已经创建,而在析构函数期间,先销毁派生类对象的成员,再销毁基类对象。

为什么要把析构函数声明为虚函数

在C++中,当一个类中包含有虚函数时,通常还需要将析构函数声明为虚函数。这是因为当使用基类指针指向派生类对象,并通过该指针调用delete运算符时,只会调用基类的析构函数,而不会调用派生类的析构函数。这会导致派生类对象中申请的资源无法被正确释放,可能会引发内存泄漏的问题。

通过在析构函数前加上virtual关键字,可以解决这个问题。这样,当通过基类指针删除派生类对象时,会首先调用派生类的析构函数,然后再调用基类的析构函数,确保派生类对象中的资源得到正确释放。

示例代码如下:


class Base {

public:

  virtual ~Base() {

    // 基类析构函数的实现

  }

};



class Derived : public Base {

public:

  virtual ~Derived() {

    // 派生类析构函数的实现

  }

};



int main() {

  Base* p = new Derived();

  delete p; // 通过基类指针删除派生类对象

  return 0;

}

在上面的示例代码中,基类Base的析构函数使用virtual关键字进行了声明。这样,在执行delete p语句时,会首先调用派生类Derived的析构函数,然后再调用基类Base的析构函数,确保派生类对象中的资源得到正确释放。

为什么不能在构造函数和析构函数中使用虚函数

在构造函数和析构函数中调用虚函数是一个容易出错的做法,并且它可能导致意想不到的行为。这是因为在构造函数和析构函数期间,对象的状态可能不完全或已经改变,并且虚函数的行为可能取决于对象的状态。

在构造函数中调用虚函数可能会导致以下问题:

  1. 虚函数的实现依赖于派生类中的成员变量,而在构造函数中,这些成员变量可能尚未被初始化,因此虚函数可能无法按预期工作。

  2. 构造函数的执行顺序是从基类到派生类,因此在构造函数期间,派生类的成员可能尚未初始化,这可能导致虚函数的行为不一致或错误。

  3. 在基类的构造函数中调用虚函数时,实际调用的是基类的虚函数,而不是派生类的虚函数,这可能导致多态性失效。

在析构函数中调用虚函数也可能导致以下问题:

  1. 调用虚函数时,派生类的数据成员可能已经被销毁,因此虚函数可能无法访问正确的数据。

  2. 在析构函数期间,对象已经进入析构状态,因此调用虚函数可能导致未定义的行为。

为避免这些问题,通常建议避免在构造函数和析构函数中调用虚函数。如果需要在对象创建和销毁期间执行特定的操作,可以考虑使用非虚函数或将这些操作放在其他地方进行处理。

本人尝试基类构造函数中调用虚函数会导致崩溃和错误,但是如果在最高级的派生类构造函数里调用虚函数,就不会有问题,此时派生类已经完成初始化,并且调用的是自身的虚函数。

如何让基类能够获取派生类中的数据

如果是通过父类指针访问派生类里的数据,可以使用下面的办法,在父类中定义缓存变量,子类中对变量写入数值




class Base {

...

protected:

    int buff;

};



class Derived : public Base {

...

public:

  void update() {

    Base::buff = xxx;

  }

};



...

Base *p = new Derived;

p->get_buff();

也可以通过虚函数间接的访问派生类变量




class Base {

...

public:

    void operate_data(){

        int a = get_data();

        ...

    }

    virtual int get_data() = 0;

};



class Derived : public Base {

...

public:

    virtual int get_data(){

        return m_data;

    }

};



...

Base *p = new Derived;

p->operate_data();