📜  C++ |虚函数|问题6(1)

📅  最后修改于: 2023-12-03 14:59:48.191000             🧑  作者: Mango

C++ |虚函数|问题6

在C++中,虚函数是一个非常有用的概念。通过使用虚函数,可以在基类中定义行为并允许子类覆盖此行为以实现其自己的行为。这个过程被称为多态。在本文中,我们将探讨使用虚函数时可能会遇到的一些问题。

问题描述

考虑以下基类:

class Base {
public:
    virtual void foo(int x) {
        std::cout << "Base::foo(" << x << ")" << std::endl;
    }
};

现在考虑另一个类,它从基类继承并覆盖了foo方法。

class Derived : public Base {
public:
    virtual void foo(int x) {
        std::cout << "Derived::foo(" << x << ")" << std::endl;
    }
};

如果我们现在有一个指向Base对象的指针,我们可以使用它来调用foo方法。根据多态的特性,实际上将调用Derived类的foo方法。

Base* b = new Derived;
b->foo(42); // 调用的是Derived::foo

首先,这段代码所执行的行为似乎是符合预期的。但是,在某些情况下,使用虚函数可能会导致一些不良的副作用。我们将在下面讨论这些问题。

问题1:非虚析构函数

考虑以下类:

class Base {
public:
    virtual void foo(int x) {
        std::cout << "Base::foo(" << x << ")" << std::endl;
    }
};

我们还没有定义析构函数,但是这不是问题,因为在这个例子中我们不需要删除这个对象。

现在考虑另一个类,它从基类继承并加入了一个成员变量。

class Derived : public Base {
public:
    Derived() {
        m_data = new int[100];
    }

    virtual void foo(int x) {
        std::cout << "Derived::foo(" << x << ")" << std::endl;
    }

private:
    int* m_data;
};

在不调用delete之前,这些对象的行为似乎是正常的。但是,如果我们忘记了删除Derived的对象,就会产生内存泄漏。如果在Derived类中定义一个析构函数并删除成员m_data,则可以避免这个问题。

class Derived : public Base {
public:
    Derived() {
        m_data = new int[100];
    }

    virtual void foo(int x) {
        std::cout << "Derived::foo(" << x << ")" << std::endl;
    }

    virtual ~Derived() {
        delete[] m_data;
    }

private:
    int* m_data;
};
问题2:多层继承

考虑以下类:

class Base {
public:
    virtual void foo(int x) {
        std::cout << "Base::foo(" << x << ")" << std::endl;
    }
};

现在添加一个Intermediate类,继承自Base,并覆盖了foo方法。

class Intermediate : public Base {
public:
    virtual void foo(int x) {
        std::cout << "Intermediate::foo(" << x << ")" << std::endl;
    }
};

现在再添加一个Derived类,它从Intermediate继承,并覆盖了foo方法。

class Derived : public Intermediate {
public:
    virtual void foo(int x) {
        std::cout << "Derived::foo(" << x << ")" << std::endl;
    }
};

如果我们现在有一个指向Intermediate对象的指针,可以调用foo方法。

Intermediate* i = new Derived;
i->foo(42); // 调用的是Derived::foo

这里看起来也符合预期。但是,如果我们直接使用本质上是基类的Base对象来调用foo方法,我们将不得不面对一个问题。

Base* b = new Derived;
b->foo(42); // 调用Intermediate::foo,不是Derived::foo
问题3:大小与性能

每个C++对象都需要存储指向虚表的指针,这使得它们变得比没有虚函数的对象更大。这可能会影响到程序的性能。在实际使用中,应该谨慎使用虚函数,并注意性能问题。

总结

本文讨论了使用虚函数时可能遇到的一些问题,包括非虚析构函数、多层继承和大小与性能。使用虚函数是C++编程中非常有用的概念,但必须谨慎使用,以避免上述问题。如果您使用虚函数时没有遇到这些问题,那么你已经在正确的方向上了!