Я борюсь с, казалось бы, простой задачей на C ++. Я пытаюсь достичь базового полиморфизма ООП.

Рассмотрим этот пример Java:

interface Bob
{
   void foo();
}

class ActualBob implements Bob
{
   void foo()
   {
      /* I like foo things */
   }
}

class Jane
{
    Bob bob;
}

У Джейн может быть любой Боб, easypeasy:

Jane jane = new Jane();
jane.bob = new ActualBob();
jane.bob.foo(); // actualbob things

Теперь, в C ++ это кажется более сложным ... Что мне нужно ввести, чтобы добиться такого поведения?

Другими словами, я хотел бы иметь переменные-члены абстрактного базового класса, но хотел бы выполнять с ними реальную реализацию.

class Bob
{
public:
   virtual void foo() = 0;
}

class ActualBob : public Bob
{
public:
    void foo(){/* I like foo things */}
}

class Jane
{
public:
    Bob bob;
}

Здесь используются ярлыки, но я бы хотел сделать это на C ++:

jane.bob.foo(); // does ActualBob things

Также, могу ли я иметь std :: vector , который содержит экземпляры ActualBob?

spinalwrap

Ответов: 3

Ответы (3)

Используйте указатели, умные указатели, например:

class Bob
{
public:
   virtual void foo() = 0;
}

class ActualBob : public Bob
{
public:
    void foo() override {/* I like foo things */}
}

class Jane
{
public:
    std::unique_ptr bob;
}

Здесь используются ярлыки, но я бы хотел сделать это на C ++:

jane.bob.foo (); // делает вещи ActualBob

You can do this:

jane.bob->foo(); // does ActualBob things, if `bob` is a pointer to ActualBob

Также, могу ли я иметь std :: vector , который содержит экземпляры ActualBob?

Да, с некоторыми изменениями, у вас может быть std :: vector (но вы должны освобождать память самостоятельно) или, что лучше, некоторые умные указатели, например std :: vector >.

Ваш код был бы в порядке, если бы Bob был неполиморфным:

class Jane
{
public:
    Bob bob;
};

Однако попытка сохранить объект polymorphich в bob приведет к нарезке объекта , что снижает полиморфизм. Для его сохранения используйте указатель или ссылку на Bob.

В зависимости от владельца Bob вы можете использовать другой тип указателя или даже ссылку. Например, если Bob распределяется динамически и может совместно использоваться несколькими экземплярами Jane, использование std :: shared_ptr будет уместным:

class Jane
{
public:
    Jane(Bob* bPtr) : bob(bPtr) {}
    std::shared_ptr bob;
};

Вы почти получили это. Проблема в том, что в Java кажется, что вам не нужно ничего использовать для создания полиморфного объекта, тогда как на самом деле в Java все эти переменные являются ссылками. Ссылки Java - это указатели C ++ с меньшими возможностями.

В C ++, когда вы набираете Bob bob;, вы фактически создаете объект точного типа слева. Если вместо этого вы используете указатель, то справа у вас может быть объект Bob (что было бы невозможно в этом случае, поскольку класс Bob имеет чистый виртуальный метод) или любой другой подкласс.

Итак, в вашем коде:

class Jane
{
public:
    Jane()
        { bob = new ActualBob(); }

    Bob * bob;
}

Однако мы без предупреждения столкнулись с другим, опасным сценарием. В Java есть сборщик мусора, что означает, что любой объект, на который нет ссылки, удаляется. В C ++ это не делается автоматически, поэтому нам приходится делать это вручную, используя оператор delete. Или, может быть, умный указатель в этом случае: они ведут себя в C ++, автоматически удаляя объект. У нас есть std :: unique_ptr <>, что и делает. На самом деле существуют и другие сценарии, такие как копирование объектов Jane, которые отчасти управляются умным указателем. Поэтому вместо того, чтобы говорить о правилах (деструкторы, конструкторы копирования, назначение операторов и семейство), давайте воспользуемся одним из них:

class Jane
{
public:
    Jane()
        { bob.reset( new ActualBob() ); }

    unique_ptr bob;
}

Интеллектуальный указатель unique_ptr <> фиксирует использование -> (технически он имеет перегруженный оператор стрелки), поэтому его использование синтаксически знакомо использованию обычного указатель. Сейчас:

Jane jane = new Jane();
jane.bob->foo();

Также, могу ли я иметь std :: vector , который содержит ActualBob экземпляры?

Конечно, но вам нужно изменить свой код таким же образом, как указано выше: вам нужно будет использовать std :: vector и самостоятельно освободить объекты, указанные членами. Или вы можете снова использовать интеллектуальный указатель, например: std :: vector >.

Надеюсь, это поможет.

2022 WebDevInsider