2015-06-10 4 views
1

Я программист на Java и был признан фактом предоставления исправления для чисто виртуальных функций. В Java я привык думать об абстрактных методах как методах, которые мы предоставим для дефиниций в базовом классе. Но следующий код вполне допустимо:Предоставление определения для чистой виртуальной функции

#include <iostream> 

struct A 
{ 
    virtual ~A() = 0; 
}; 

A::~A(){ std::cout << "~A()" << std::endl; } 

struct B : A 
{ 
    ~B(){ std::cout << "~B()" << std::endl; } 
}; 

A *a = new B; 


int main() 
{ 
    delete a; 
} 

Но если мы пытаемся сделать что-то вроде этого:

#include <iostream> 

struct A 
{ 
    virtual ~A() = 0; 
    virtual void foo() = 0; 
}; 

void A::foo(){ } 
A::~A(){ std::cout << "~A()" << std::endl; } 

struct B : A 
{ 
    ~B(){ std::cout << "~B()" << std::endl; } 
}; 

A *a = new B; 


int main() 
{ 
    delete a; 
} 

Компилятор собирается complaing о не предоставляя определения для чисто виртуальных функций. Почему мы можем определить чисто виртуальный деструктор в области namesapace, но не можем сделать это для обычной функции-члена.

Это скорее исключение, чем правило?

+1

Что? Предоставление определения для чистой виртуальной функции совершенно законно. Компилятор никогда не будет жаловаться на это. Единственная проблема с вашим вторым кодом заключается в том, что вы пытаетесь создать экземпляр абстрактного класса 'B', а не для обеспечения определенной функции. – AnT

+0

@AnT Но я сделал это в области пространства имен. Я дал это определение, что случилось? –

+0

Где? Я не вижу определения для 'B :: foo' где угодно. 'A :: foo' - чистая виртуальная функция. Если вы не отмените его с помощью 'B :: foo', он останется чистым и в' B'. – AnT

ответ

4

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

+0

Итак, класс перестает быть абстрактным, если мы переопределяем его чистые виртуальные функции? Но в случае деструктора мы этого не сделали, мы предоставили определение виртуального деструктора. –

+0

@ St.Antario: Да, класс перестает быть абстрактным, когда вы переопределяете все его виртуальные функции с нечистыми функциями. Что именно означает ваше «мы не сделали этого»? Да, мы сделали это. Когда вы написали нечистый деструктор в 'B', вы * перевернули * чистый деструктор' A'. Несмотря на то, что имена деструкторов различны, деструкторы все еще всегда переопределяют друг друга. Они особенны в этом отношении. – AnT

+0

@ St.Antario Да, если в классе 'B' вы объявляете все свои функции-члены нечистыми и переопределяете все чистые функции-члены, унаследованные от базовых классов, то' B' не будет абстрактным. Обратите внимание, что для деструктора это происходит автоматически, когда вы не объявляете его самостоятельно. – Brian

2

Вы, по-видимому, путаетесь в причине ошибки во второй версии кода.

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

Определение для A::foo, которое вы указали во второй версии кода, совершенно законно и не вызывает никаких жалоб от компилятора. Единственная проблема с вашим вторым кодом заключается в том, что B не переопределяет foo. Следовательно, B по-прежнему является абстрактным классом. И вы пытаетесь создать его.

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

Во второй версии кода B наследует две чистые виртуальные функции от A - деструктор и foo. Поскольку вы не переопределили foo в B, B по-прежнему является абстрактным классом и не может быть создан.

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

+0

Подождите, я предоставил определение для функции виртуального члена, поэтому A nd B не является loger Абстрактные. –

+1

@ St.Antario: Нет. Обеспечение тела для чистой виртуальной функции не «не дает» этой функции. Функция остается чистой, а класс остается абстрактной. Класс 'A' является абстрактным в обеих версиях кода. Класс 'B' является абстрактным во второй версии кода. – AnT

1

Когда я компилирую вышеуказанную программу, используя г ++ 4.8.2, я получаю следующие сообщения:

команды

Компилятор:

g++ -Wall -std=c++11  socc.cc -o socc 

Сообщения об ошибках:

socc.cc:17:12: error: cannot allocate an object of abstract type ‘B’ 
A *a = new B; 
      ^
socc.cc:12:8: note: because the following virtual functions are pure within ‘B’: 
struct B : A 
     ^
socc.cc:9:6: note: virtual void A::foo() 
void A::foo(){ } 
    ^
make: *** [socc] Error 1 

Суть сообщение об ошибке состоит в том, что B является абстрактным типом, поскольку он не обеспечивает переопределения void foo();, который объявлен как чистая виртуальная функция в базовом классе.

Определение A::foo() не является незаконным. Следующая программа работает нормально:

#include <iostream> 

struct A 
{ 
    virtual ~A() = 0; 
    virtual void foo() = 0; 
}; 

void A::foo(){ } 
A::~A(){ std::cout << "~A()" << std::endl; } 

struct B : A 
{ 
    ~B(){ std::cout << "~B()" << std::endl; } 
    void foo() {} // Need this to be able to instantiate B. 
}; 

A *a = new B; 

int main() 
{ 
    delete a; 
} 
Смежные вопросы