Почему C ++ не может инициализировать переменную типа «производный класс» со значением типа «супер класс»? - PullRequest
2 голосов
/ 15 июля 2011

Пожалуйста, примите во внимание приведенный ниже код:

class a
{
    int a1;

    public:
    a()
    {
        printf("foo1\n");
    }
};

class b : public a
{
    int a2;
    public:
    b()
    {
        printf("foo2\n");
    }
};
int main (int argc, const char * argv[])
{
    b *instance = new a();
    return 0;
}

Это выдает ошибку: невозможно инициализировать переменную типа "b *" со значением типа "a *". Отлично работает, когда я пишу

a *instance = new b();

Вывод:

foo1
foo2

Может кто-нибудь объяснить, пожалуйста, причину?Я был бы чрезвычайно признателен:)

Другое дело, если я напишу

instance->~a();

выше return 0;, ничего лишнего не произойдет.Это потому, что конструктор может быть вызван только один раз?

Ответы [ 9 ]

6 голосов
/ 15 июля 2011

b - это a.
a не b.


Вы можете поместить экземпляр типа Giraffe в переменную типа Animal.
Однако вы не можете поместить экземпляр Animal в переменную типа Giraffe (Что, если это Porcupine?)

4 голосов
/ 15 июля 2011

Давайте сделаем это немного более конкретным:

class Animal
{
    int a1;

    public:
    Animal()
    {
        printf("Animal\n");
    }
};

class Bat : public Animal
{
    int a2;
    public:
    Bat()
    {
        printf("bat\n");
    }
};
int main (int argc, const char * argv[])
{
    Bat *instance = new Animal();
    return 0;
}

Теперь вы видите, почему это может быть недействительным? То, что вы создаете с помощью new Animal(), может быть любым типом животных. Может быть недопустимо назначать его переменной Bat, потому что это может быть не летучая мышь.

4 голосов
/ 15 июля 2011

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

class Animal {
    void eat();
};

class Dog : public Animal {
    void bark();
}

Имеет смысл рассматривать Dog как универсальный Animal, но для универсального Animal не будет разумных действий, если он будет заказан на bark.

1 голос
/ 15 июля 2011

Помните об этом всегда. Вы не можете назначить объект базового класса указателю производного класса.[Производный объект класса] является [Базовым объектом класса].Обратное неверно.

1 голос
/ 15 июля 2011

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

Это небезопасный состав.

1 голос
/ 15 июля 2011

Поскольку a не является b, вы не можете назначить указатель на b для объекта типа a.

Так как b - это a, с другой стороны, он действительно работает нормально.

0 голосов
/ 15 июля 2011

Относительно деструктора -

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

Кроме того, указатель / ссылка производного класса совместим по типу с указателем / ссылкой базового класса. Но в противном случае это не так. (Нет переходного характера)

В вашем случае -

a *instance = new b();  // This should be correct way.

Статический тип instance равен a*, а динамический тип - b*. Итак, a действует как базовый класс, и его деструктор должен быть виртуальным .

0 голосов
/ 15 июля 2011

Вы не можете заменить a на b, потому что a не обладает всеми функциями b. Таким образом, нет смысла делать такое назначение.

Что касается вашей второй части, в 99% случаев вы говорите delete var, чтобы уничтожить что-то, выделенное с помощью new вместо явного вызова деструктора, что является продвинутой темой.

0 голосов
/ 15 июля 2011

Проблема в том, что отношения "это". Объект class a не является объектом class b, поэтому

b *instance = new a(); // won't work

будет означать, что вы пытаетесь установить указатель на class b на что-то, что не является объектом class b. В то же время вы можете сделать наоборот:

a* instance = new b(); //will work

, поскольку объекты class b также являются объектами class a, и поэтому здесь вы должны установить указатель на class a на то, что действительно является объектом class a.

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