У меня есть некоторые проблемы с виртуальными классами и инкапсуляцией.
Рассмотрим следующий минимальный пример программы на C ++:
#include <iostream>
class IConnection
{
public:
virtual void connect() = 0;
virtual std::string recv() = 0;
virtual void disconnect() = 0;
virtual ~IConnection() {}
};class ConcreteConnection: public IConnection
{
public:
ConcreteConnection(): m_connected(false) {}
void connect() { m_connected = true; }
std::string recv() { return "Received some text."; }
void disconnect() { m_connected = false; }
private:
bool m_connected;
};
class Container
{
public:
Container() { m_connection = NULL; }
void SetConnection(IConnection *connection) { m_connection = connection; };
void GetData() { std::cout << m_connection->recv() << std::endl; }
~Container() { delete m_connection; }
private:
IConnection *m_connection;
};
int main(void)
{
Container container;
ConcreteConnection *connection = new ConcreteConnection();
container.SetConnection(connection);
container.GetData();
return 0;
}
Этот простой пример отлично работает, но я не совсем доволен этим.
Контейнер-объект должен владеть соединением, не беспокоясь
конкретная реализация интерфейса IConnection. Вот почему я создал
ConcreteConnection-объект вне контейнера. Что мне не нравится, так это
Я должен передать указатель или ссылку на соединение. Я хотел бы пройти
копия объекта соединения, так что основная функция не имеет
возможность манипулировать или удалять объект соединения после передачи его
контейнер. Но, насколько я знаю, невозможно передать копию
подключение, не сообщая контейнер, к которому конкретная реализация
IConnection это принадлежит.
Так у вас есть идеи, как решить эту проблему? Можно ли как-то пройти
Копирование объекта в любую функцию без указания функции, к которой
конкретная реализация интерфейса, к которому принадлежит объект?
Я относительно новичок в C ++ и ООП, поэтому не стесняйтесь и скажите мне, если мой
структура класса совершенно неверна, и этот случай не встречается в реальной жизни
программный код (и как он должен работать вместо).
Заранее спасибо.
Вот как я мог бы написать это в современном C ++:
#include <memory>
#include <type_traits>
class Container
{
std::unique_ptr<IConnection> ptr;
explicit Container(IConnection * p) : ptr(p) { }
public:
template <typename T, typename ...Args>
static typename std::enable_if<std::is_base_of<IConnection, T>::value, Container>::type
make(Args &&... args)
{
return Container(new T(std::forward<Args>(args)...));
}
};
Использование:
int main()
{
auto c1 = Container::make<ConcreteConnection>();
auto c2 = Container::make<TCPConnection>("127.0.0.1", 8080);
auto c3 = Container::make<LocalPipeConnection>("/tmp/pipe");
}
Подумайте об этом так: все разные конкретные классы, наследуемые от абстрактного класса, имеют разных членов и т. Д. Так что, если вы не укажете компилятору, какой тип объекта вы передаете, как он узнает, какой объем памяти выделить, и т. Д. .?
Поэтому, когда вы пишете функцию, которая принимает указатель на абстрактный класс в качестве аргумента, компилятор точно знает, сколько места ему нужно выделить для этого аргумента: размер одного указателя. Если вы попытаетесь сделать аргумент экземпляром абстрактного класса, а не указателем, компилятор не будет знать, сколько памяти выделить, потому что это зависит от того, какой производный класс будет передан в функцию.