2

I have some problems with virtual classes and encapsulation. Consider the following minimal example of a C++ program:

#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;
}

This simple example works fine, but I am not totally happy with that. The Container-object should own the connection, without being bothered by the concrete implementation of the interface IConnection. That's why I created the ConcreteConnection-object outside of the container. What I don't like is that I have to pass a pointer or reference of the connection. I would like to pass a copy of the connection-object, so that the main-function does not have any chance to manipulate or delete the connection-object after passing it to the container. But as far as I know it is impossible to pass a copy of the connection, without telling the container to which concrete implementation of IConnection it belongs.

So do you have any idea how to solve this? Is it somehow possible to pass a copy of an object to any function without telling the function to which specific implementation of an interface the object belongs to?

I'm relatively new to both C++ and OOP, so don't hesitate and tell me, if my class struct is completely wrong and this case does not occur in real-life programming code (and how it should work instead).

Thanks in advance.

lslah
  • 556
  • 8
  • 17
  • 2
    Actually you can implement a copy protocol within the connection interface as a virtual method (say `clone` or `copy`), which would do the actual job. But more generally, if you want to keep the object from "leaking" in other parts of your code base (`main` in your example), you can implement the dependency injection pattern, by the use of a "factory" singleton whose purpose is to build and deliver the connection instance. You willhave delimited the existence of the instance to the final owner and the factory method building it, and you will also avoid the need of a copy. – didierc Jun 14 '13 at 19:42
  • That sounds pretty nice! Consider making this an answer, so that I can accept it. – lslah Jun 14 '13 at 19:54
  • 1
    I would personally pick Kerrek SB's answer, as it solves the issue the C++ way, by moving the construction of the object directly in the container. It's exactly what you need, and it implements the injection pattern as well (the container doesn't know which concrete class get instantiated). In my solution, that knowledge was delimited to a factory method, but you still had to make sure the instance wasn't being kept by it somehow (since that was your original concern). Note that you can still put the call to `make` in another place than `main` (for instance a class dedicated to the app setup). – didierc Jun 14 '13 at 21:20
  • Thank you for the compliment though! – didierc Jun 14 '13 at 21:24
  • 1
    You might also want to make the connection constructors protected, with only the container as a friend class, to ensure that they don't get constructed elsewhere. Maybe there's a more modern C++ way for that as well though. – didierc Jun 14 '13 at 21:28

2 Answers2

2

Here's how I might write this in modern 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)...));
    }
};

Usage:

int main()
{
    auto c1 = Container::make<ConcreteConnection>();
    auto c2 = Container::make<TCPConnection>("127.0.0.1", 8080);
    auto c3 = Container::make<LocalPipeConnection>("/tmp/pipe");
}
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • At first I wasn't able to fully understand your code, because I've never dealt with 'modern C++'. But after didierc pointed out that your code is actually a very nice solution, I did some research and are now able to understand it. Thanks a lot for your answer! Do you know about any good introduction to modern C++? I read about the book by Andrei Alexandrescu. Is it worth it's money? Maybe there are some good and free online tutorials as first introduction? Maybe I should start reading about templates? – lslah Jun 17 '13 at 06:58
  • 1
    @Islah check out the [book list](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) maintained here for C++ (you might want to have a look at the info tab for C++ as well, plenty of interesting links). – didierc Jun 19 '13 at 23:19
0

Think about it this way: all of the different concrete classes that inherit from an abstract class have different members, etc. So unless you tell the compiler what kind of object you are passing around, how will it know how much memory to allocate, etc.?

So when you write a function that takes a pointer to an abstract class as an argument, the compiler knows exactly how much space it needs to allocate for that argument: the size of one pointer. If you try to make the argument an instance of an abstract class rather than a pointer, the compiler does not know how much memory to allocate because it depends on which derived class would be passed into the function.

Daniel
  • 6,595
  • 9
  • 38
  • 70
  • But I thought that's how Virtual Classes/Interfaces work. That other classes don't need to know anything about the specific implementation of an interface. And about your last thought: If my main-function makes a copy it's still able to manipulate the connection, which the container owns. It's not about getting a copy of the object, but about preventing that the main is able to manipulate the connection after passing it to the container. – lslah Jun 14 '13 at 19:40