2010-01-18 3 views
131

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

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

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

Действительно ли код выше ОК?

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

+6

Поскольку вы не имеете никакого контроля доступа, указанный для 'F()' в объявлении класса 'Ā',' A :: f() '' дефолтов в private'. Возможно, это не то, что вы хотите. –

ответ

168

Функция чистого 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(); 
    } 

}; 

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

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

+6

Мои мысли точно ... за исключением более красноречиво, чем у меня. – Dan

+0

Вы забыли добавить, почему это удивляет программистов: это потому, что встроенное определение запрещено стандартом. Чистые определения виртуальных методов должны быть депортированы. (либо в .inl или .cpp, чтобы ссылаться на обычные методы именования файлов). –

+0

так что этот метод вызова такой же, как и вызов статического метода. Какой-то метод класса в Java. –

16

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

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

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

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

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

Base::~Base() {} 

class Derived : public Base {}; 

int main() { 
    // Base b; -- compile error 
    Derived d; 
} 
+1

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

+1

Этот код неправильный. Вы должны определить dtor вне определения класса из-за синтаксического причуда языка. – 2010-01-18 21:19:19

+0

@Roger: спасибо, это действительно помогло мне - это код, который я использовал, он компилируется под MSVC, но, я думаю, он не будет переносимым. –

19

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

4

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

Скотт Мейерс обсуждает это в Эффективный C++ (2-е издание) Пункт # 36 Различают наследование интерфейса и наследование реализации. Номер позиции, возможно, изменился в последнем выпуске.

57

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

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

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

+9

Это неверно. Если вы вызываете эту чистую виртуальную функцию в конструкторе вашего чистого виртуального класса, будет создан чистый виртуальный вызов. В этом случае вам лучше реализовать. – rmn

+0

@rmn, Да, вы правы в виртуальных вызовах в конструкторах. Я обновил ответ. Надеюсь, все знают, что этого не делать.:) –

+2

Фактически, создание базового чистого вызова от конструктора приводит к поведению, определяемому реализацией. В VC++ это приводит к сбою _purecall. –

4

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

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

1

«virtual void 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 :: бар().

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

Надеется, что я мог бы помочь :-)

+5

Он не должен быть реализован во всех производных классах, но он ДОЛЖЕН иметь реализацию во всех производных классах, которые вы собираетесь создавать. Вы не можете создать экземпляр объекта типа 'C' в вашем примере. Вы можете создать объект типа 'D', потому что он получает свою реализацию' foo' от 'B'. – YoungJohn

Смежные вопросы