Как я могу получить виртуальное наследование правильно? - PullRequest
0 голосов
/ 29 января 2020

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

Как правильно определить определение метода и таблицы и как избежать эффекта среза. Я слишком долго занимался Python, где бы что-то подобное работало.

#include <iostream>
#include <vector>


using namespace std;

class A{
    public:

        virtual string print(){return string("A");};
};

class B: public A{
    virtual string print() final {return string("B");};
};

class C: public A{
    virtual string print() final {return string("C");};
};

int main()
{

   vector<A> v;
   v.push_back(B());
   v.push_back(C());

   for(auto x : v){
       cout << x.print() << endl;
   }

}

=>

$g++ -std=c++11 -o main *.cpp
$main
A
A

Ответы [ 2 ]

5 голосов
/ 29 января 2020

Давайте посмотрим на ваш код:

vector<A> v;
v.push_back(B());
v.push_back(C());

Здесь push_back принимает аргумент типа A&& и перемещает его, используя конструктор перемещения A, для создания нового элемента вектор. Итак, ваш код:

  1. Создает вектор A s
  2. Создает экземпляр B, затем вызывает конструктор перемещения A для этого экземпляра B для создания экземпляра A (который не является экземпляром B) в векторе
  3. То же самое для C вместо B

Если вы хотите использовать динамическую диспетчеризацию c, вам нужно хранить указатели на ваши элементы в векторе. Выделите объекты в куче и сохраните для них маркеры, используя std::unique_ptr, что освободит память в его деструкторе.

// for std::unique_ptr, available since C++11, and std::make_unique, available since C++14
#include <memory>

// ...

std::vector<std::unique_ptr<A>> v;
v.push_back(std::make_unique<B>());
v.push_back(std::make_unique<C>());

Теперь, когда вектор использует std::unique_ptr, вам нужно использовать & при зацикливании вектора:

for(auto& x : v) {
    std::cout << x->print() << std::endl;
}

auto не будет автоматически ставить ссылку. Если вы просто используете auto, то каждый элемент вектора будет скопирован в x, но поскольку конструктор копирования std::unique_ptr удален, код не будет компилироваться.

1 голос
/ 29 января 2020

Объекты любого дочернего объекта хранятся в векторе родительского элемента.

Нет. Вектор родителя может хранить только родительские объекты. Родительские объекты не являются дочерними объектами.

Циклы по вектору Я вижу только вызовы методов из родительского класса.

Это потому, что вектор содержит родительские объекты.

Как правильно определить определение метода и таблицы vtables

Нет ничего плохого в определениях функций-членов или vtables.

и как Избегайте эффекта среза.

Эффект среза можно избежать, не отрывая базовый подобъект от производного объекта. Динамический полиморфизм c возможен только через косвенное обращение. С помощью указателя (или ссылки) вы можете указать на базовый объект, который может быть базовым подобъектом различных производных объектов. Пример:

B b;
C c;
A* a;

a = &b;
a->print(); // dynamic dispatch invokes B::print
a = &c;
a->print(); // dynamic dispatch invokes C::print

A sliced = b;
sliced.print(); // static dispatch invokes A::print
                // sliced is an individual object that is not
                // base sub object of another
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...