Полиморфизм с умным указателем - PullRequest
3 голосов
/ 18 июня 2020

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

============== Исходный вопрос ============= =====

Я пытаюсь заставить умный указатель работать с полиморфизмом. В следующем коде прототипа реализация чистой virtual функции Base::print() должна находиться в блоке памяти объекта Derived. DerivedWrap имеют доступ к указателю на объект Derived.

Почему DerivedWrap::print() не может получить доступ к реализации функции?

using namespace std;

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

class Derived : public Base 
{
public:
    Derived(int in) : i(in) {}

    void print() {
        cout << "int is " << i << endl;
    }

private:
    int i;
};

class DerivedWrap 
{
public:
    DerivedWrap() : DerivedWrap(make_unique<Derived>(2)) {}
    DerivedWrap(unique_ptr<Base> pBase) : _pBase(move(pBase)) {}

    void print()
    {
        _pBase->print();
    }

private:
    unique_ptr<Base> _pBase;
};

int main() 
{
    DerivedWrap pDW1();
    pDW1->print(); // error: request for member ‘print’ in ‘pDW1’, which is of non-class type ‘DerivedWrap()’

    DerivedWrap pDW2(make_unique<Derived>(2));
    pDW2->print(); // error: base operand of ‘->’ has non-pointer type ‘DerivedWrap’
    return 0;
}

Ответы [ 3 ]

7 голосов
/ 18 июня 2020

У вас есть пара проблем.

  • Это DerivedWrap pDW1(); - объявление функции с типом возвращаемого значения DerivedWrap. Он не вызывает свой конструктор по умолчанию, которого вы ожидаете. Вам нужно просто
    DerivedWrap pDW1;  // calls the default constructor
    // or 
    // DerivedWrap pDW1{};
    
  • Во-вторых, pDW1 - это просто объект DerivedWrap. Поэтому звонить по номеру operator-> не нужно. Короче надо
    DerivedWrap pDW1;
    pDW1.print(); 
    
    То же самое и с pDW2. Вам понадобится
    DerivedWrap pDW2(std::make_unique<Derived>(2));
    pDW2.print();
    
  • И последнее, но не менее важное, Base обязательный деструктор virtual для определенного поведения. Подробнее: Когда использовать виртуальные деструкторы?

Короче говоря, вам нужно

#include <iostream>
#include <memory>

class Base
{
public:
   virtual void print() = 0;
   virtual ~Base() = default;  // provide virtual destructor
};

class Derived /*final*/: public Base
{
public:
   // ... other code

   void print() override // recommended to override the virtual functions
   {
      std::cout << "int is " << i << std::endl;
   }
private:
   int i;
};

class DerivedWrap /* final */
{
public:
   // ...other code

   void print()
   {
      _pBase->print();
   }

private:
   std::unique_ptr<Base> _pBase;
};

int main()
{
   DerivedWrap pDW1; // or DerivedWrap pDW1{};
   pDW1.print();

   DerivedWrap pDW2{ std::make_unique<Derived>(2) };
   pDW2.print();
}

В качестве примечания, пожалуйста, не тренируйтесь с using namespace std;

4 голосов
/ 18 июня 2020

У вас есть несколько опечаток, это должно быть:

int main()
{
  DerivedWrap pDW1; // object instantiation, no () needed
  pDW1.print(); //no dereferencing required

  DerivedWrap pDW2(make_unique<Derived>(2));
  pDW2.print(); // again, no dereference required

  return 0;
}

Еще одно примечание, для объектов polymorphi c вам понадобится виртуальный деструктор в вашем базовом классе.

2 голосов
/ 18 июня 2020

Это не имеет ничего общего с полиморфизмом, виртуальными функциями или интеллектуальными указателями.

Вы только что сделали две небольшие типографские ошибки:

  1. DerivedWrap pDW1(); объявляет функцию . Удалите указатели разыменования ().
  2. ->, но ни pDW1, ни pDW2 не являются функцией. Вместо этого используйте ..
...