чисто виртуальная функция с реализацией - PullRequest
159 голосов
/ 18 января 2010

Я понимаю, что для чисто виртуальной функции нет реализации, однако мне сказали, что может быть реализация для чисто виртуальной функции.

class A {
public:
    virtual void f() = 0;
};

void A::f() {
    cout<<"Test"<<endl;
}

Код выше OK?

Какова цель сделать ее чисто виртуальной функцией с реализацией?

Ответы [ 8 ]

194 голосов
/ 19 января 2010

Чистая virtual функция должна быть реализована в производном типе, который будет непосредственно создан, однако базовый тип все еще может определять реализацию.Производный класс может явно вызывать реализацию базового класса (если это разрешено правами доступа), используя полностью заданное имя (вызывая A::f() в вашем примере - если A::f() было public или protected).Что-то вроде:

class B : public A {

    virtual void f() {
        // class B doesn't have anything special to do for f()
        //  so we'll call A's

        // note that A's declaration of f() would have to be public 
        //  or protected to avoid a compile time problem

        A::f();
    }

};

Вариант использования, который я могу придумать, - это когда более или менее разумное поведение по умолчанию, но разработчик класса хочет такое поведение по умолчаниювызываться только явно.Это также может быть случай, когда вы хотите, чтобы производные классы всегда выполняли свою собственную работу, но при этом могли вызывать общий набор функций.

Обратите внимание, что, хотя это разрешено языком, это не то, что яувидеть часто используемые (и тот факт, что это можно сделать, кажется, удивляет большинство программистов на C ++, даже опытных).

70 голосов
/ 19 января 2010

Чтобы было ясно, вы неправильно понимаете, что = 0; после виртуальной функции означает.

= 0 означает, что производные классы должны предоставлять реализацию, а не то, что базовый класс не может обеспечить реализацию.

На практике, когда вы помечаете виртуальную функцию как чистую (= 0), нет особого смысла в предоставлении определения, потому что оно никогда не будет вызываться, если кто-то явно не делает это через Base :: Function (...) или если конструктор базового класса вызывает соответствующую виртуальную функцию.

20 голосов
/ 18 января 2010

Преимущество этого метода в том, что он заставляет производные типы по-прежнему переопределять метод, но также обеспечивает реализацию по умолчанию или аддитивную реализацию.

17 голосов
/ 18 января 2010

Если у вас есть код, который должен быть выполнен производным классом, но вы не хотите, чтобы он выполнялся напрямую, - и вы хотите, чтобы он был переопределен.

Ваш код верен, хотя в целом это не часто используемая функция, и обычно она появляется только при попытке определить чистый виртуальный деструктор - в этом случае вы должны предоставить реализацию. Самое смешное, что когда вы выходите из этого класса, вам не нужно переопределять деструктор.

Следовательно, единственное разумное использование чисто виртуальных функций - указание чистого виртуального деструктора в качестве ключевого слова «не конечный».

Следующий код на удивление правильный:

class Base {
public:
  virtual ~Base() = 0;
};

Base::~Base() {}

class Derived : public Base {};

int main() { 
  // Base b; -- compile error
  Derived d; 
}
5 голосов
/ 19 января 2010

Вы должны отдать тело чисто виртуальному деструктору, например:)

Чтение: http://cplusplus.co.il/2009/08/22/pure-virtual-destructor/

(ссылка не работает, используйте архив)

4 голосов
/ 09 апреля 2010

Чистые виртуальные функции с или без тела просто означают, что производные типы должны обеспечивать свою собственную реализацию.

Чистые тела виртуальных функций в базовом классе полезны, если ваши производные классы хотят вызвать реализацию вашего базового класса..

4 голосов
/ 19 января 2010

Да, это правильно.В вашем примере классы, производные от A, наследуют как интерфейс f (), так и реализацию по умолчанию.Но вы заставляете производные классы реализовывать метод f () (даже если он только вызывает реализацию по умолчанию, предоставленную A).

Скотт Мейерс обсуждает это в Effective C ++ (2nd Edition) Пункт № 36 Различать наследование интерфейса и наследование реализации.Номер позиции мог измениться в последней редакции.

2 голосов
/ 24 марта 2015

Виртуальная пустота foo () = 0; синтаксис не означает, что вы не можете реализовать foo () в текущем классе, вы можете. Это также не означает, что вы должны реализовать его в производных классах . Прежде чем дать мне пощечину, давайте рассмотрим Алмазную проблему: (Неявный код, имейте в виду).

class A
{
public: 
    virtual void foo()=0;
    virtual void bar();
}

class B : public virtual A
{
public:
    void foo() { bar(); }
}

class C : public virtual A
{
public:
    void bar();
}

class D : public B, public C
{}

int main(int argc, const char* argv[])
{
    A* obj = new D();
    **obj->foo();**
    return 0;
}

Теперь вызов obj-> foo () приведет к B :: foo (), а затем к C :: bar ().

Вы видите ... чистые виртуальные методы не должны быть реализованы в производных классах (foo () не имеет реализации в классе C - компилятор скомпилирует) В C ++ много лазеек.

Надеюсь, я смогу помочь: -)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...