Почему мы не можем создавать объекты для абстрактного класса в C ++? - PullRequest
6 голосов
/ 19 января 2010

Я знаю, что это не разрешено в C ++, но почему? Что, если это будет разрешено, какие будут проблемы?

Ответы [ 9 ]

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

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

Сами функции не содержат памяти в классе. Следующий класс:

struct dumb_class
{
    void foo(){}
    void bar(){}
    void baz(){}
    // .. for all eternity

    int i;
};

Имеет размер int. Независимо от того, сколько у вас функций, этот класс будет занимать только пространство, необходимое для работы на int. Когда вы вызываете функцию в этом классе, компилятор передаст вам указатель на место, где хранятся данные в классе; это указатель this.

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

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

Возьмем этот класс и предположим, что наш разработчик использует vtables:

struct base { virtual void foo(void); };
struct derived { virtual void foo(void); };

Компилятору потребуется создать две vtables, одну для базы и одну для производной. Они будут выглядеть примерно так:

typedef /* some generic function pointer type */ func_ptr;

func_ptr __baseTable[] = {&base::foo}; 
func_ptr __derivedTable[] = {&derived::foo}; 

Как он использует эту таблицу? Когда вы создаете экземпляр класса выше, компилятор вставляет скрытый указатель, который будет указывать на правильный vtable. Поэтому, когда вы говорите:

derived d;
base* b = &d;
b->foo();

После выполнения последней строки она переходит к правильной таблице (__derivedTable в этом случае), переходит к правильному индексу (0 в данном случае) и вызывает эту функцию. Как вы можете видеть, это в итоге вызовет derived::foo, что и должно произойти.

Обратите внимание, что позже это то же самое, что и derived::foo(b), передавая b как указатель this.

Итак, когда присутствуют виртуальные методы, класс размера будет увеличиваться на один указатель (указатель на vtable.) Множественное наследование немного меняет это, но в основном это то же самое. Вы можете получить более подробную информацию на C ++ - FAQ .

Теперь к вашему вопросу. У меня есть:

struct base { virtual void foo(void) = 0; }; // notice the = 0
struct derived { virtual void foo(void); };

и base::foo не имеют реализации. Это делает base::foo чистой абстрактной функцией. Итак, если бы я назвал это, как указано выше:

derived d;
base* b = &d;
base::foo(b);

Какое поведение мы должны ожидать? Будучи чисто виртуальным методом, base::foo даже не существует. Приведенный выше код является неопределенным поведением и может делать что угодно, от ничего до сбоя, с чем-то промежуточным. (Или хуже.)

Подумайте, что представляет собой чистая абстрактная функция. Помните, функции не принимают данных, они только описывают, как манипулировать данными. Чистая абстрактная функция говорит: «Я хочу вызвать этот метод и манипулировать моими данными. Как это сделать, решать только вам».

Поэтому, когда вы говорите: «Хорошо, давайте назовем абстрактный метод», вы отвечаете на вышеизложенное: «До меня? Нет, вы делаете это». на что он ответит "@ # ^ @ # ^". Просто не имеет смысла говорить кому-то, кто говорит «делай это», «нет».

Чтобы ответить на ваш вопрос напрямую:

"почему мы не можем создать объект для абстрактного класса?"

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

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

Проблема заключается просто в следующем:

  • что должна делать программа, когда вызывается абстрактный метод ?
  • и даже хуже: что должно быть возвращено для не пустой функции ?

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

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

Потому что логически это не имеет никакого смысла.

Абстрактный класс - это неполное описание.
Он указывает, что нужно заполнить, чтобы завершить его, но без этих битов его нет.завершено.

Моим первым примером была игра в шахматы:
В игре много фигур разного типа (Король, Королева, Пешка и т. д.).

Но их нетфактические объекты типа piece, но все объекты являются экземплярами объектов, производных от piece.Как вы можете иметь объект чего-то, что не полностью определено.Нет смысла создавать объект из кусочка, так как игра не знает, как он движется (это абстрактная часть).Он знает, что может двигаться, но не знает, как он это делает.

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

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

Обычно, однако, абстрактные классы имеют абстрактные методы. Их нельзя создать по той простой причине, что им не хватает этих методов.

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

Абстрактные классы не являются экземплярами по определению . Они требуют, чтобы были производные, конкретные классы. Чем еще был бы абстрактный класс, если бы у него не было чисто виртуальных (не реализованных) функций?

1 голос
/ 19 мая 2012

Почему мы не можем создать объект абстрактного класса?

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

0 голосов
/ 13 декабря 2014

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

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

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

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

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

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

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

...