C++ 虚拟接口与封装

C++ 虚拟接口与封装,c++,class,interface,encapsulation,virtual-functions,C++,Class,Interface,Encapsulation,Virtual Functions,我在虚拟类和封装方面有一些问题。 考虑下面的C++程序的最小例子: #include <iostream> class IConnection { public: virtual void connect() = 0; virtual std::string recv() = 0; virtual void disconnect() = 0; virtual ~IConnection() {} }; cla

我在虚拟类和封装方面有一些问题。 考虑下面的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;
}
#包括
I类连接
{
公众:
虚空连接()=0;
虚拟std::string recv()=0;
虚拟void disconnect()=0;
虚拟~IConnection(){}
};
类ConcreteConnection:公共IConnection
{
公众:
ConcreteConnection():m_connected(false){
void connect(){m_connected=true;}
std::string recv(){return“收到一些文本。”;}
void disconnect(){m_connected=false;}
私人:
布尔穆连通;
};
类容器
{
公众:
容器(){m_connection=NULL;}
void SetConnection(IConnection*connection){m_connection=connection;};

void GetData(){std::cout recv()这样想:从抽象类继承的所有不同的具体类都有不同的成员,等等。因此,除非你告诉编译器你传递的是什么类型的对象,否则它怎么知道要分配多少内存,等等


因此,当您编写一个函数,将指向抽象类的指针作为参数时,编译器确切地知道它需要为该参数分配多少空间:一个指针的大小。如果您试图使该参数成为抽象类的实例而不是指针,编译器不知道要分配多少内存,因为t取决于哪个派生类将被传递到函数中。

以下是我在现代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)...));
    }
};
#包括
#包括
类容器
{
std::唯一的ptr ptr;
显式容器(IConnection*p):ptr(p){}
公众:
模板
静态类型名称std::enable_if::type
make(Args&…Args)
{
返回容器(新T(标准::转发(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");
}
intmain()
{
auto c1=容器::make();
自动c2=容器::make(“127.0.0.1”,8080);
auto c3=容器::make(“/tmp/pipe”);
}

但我认为虚拟类/接口就是这样工作的。其他类不需要知道任何关于接口的具体实现的信息。还有你最后的想法:如果我的主函数复制了一个副本,它仍然能够操作容器拥有的连接。这不是获取对象的副本,而是实际上,你可以在连接接口中实现一个复制协议,作为一个虚拟方法(比如
clone
copy
),这将完成实际的工作。但是更一般地说,如果你想防止对象“泄漏”在代码库的其他部分(
main
在您的示例中),您可以通过使用“工厂”来实现依赖项注入模式SuntLon,其目的是构建和传递连接实例。您可以将实例的存在分隔到最终所有者和构建它的工厂方法中,并且您也将避免需要复制。这听起来很不错!考虑一下这个答案,这样我就可以接受它。我个人会选择Kerrek SB的答案,因为它解决了C++的方式,通过直接在容器中移动对象的构造,这正是你需要的,并且它实现了注入模式(容器不知道实例化哪个具体类)。。在我的解决方案中,该知识被限定为工厂方法,但您仍然必须确保实例没有被它以某种方式保留(因为这是您最初关心的问题)。请注意,您仍然可以将对
make
的调用放在
main
以外的其他位置(例如,专用于应用程序设置的类)谢谢你的赞美!你可能也想让连接构造器被保护,只把容器当作朋友类,以确保它们不会在别处被构建。也许还有一个更现代的C++方式。起初我不能完全理解你的代码,因为我从来没有处理过“模式”。但是,在迪迪尔指出你的代码实际上是一个很好的解决方案之后,我做了一些研究,现在已经能够理解它了。非常感谢你的答案!你知道关于C++ C++的好的介绍吗?我读过Andrei Alexandrescu的书。它值得吗?也许有一些好的免费的在线教程。作为第一个介绍?也许我应该开始阅读模板?@ ISLAH在这里检查C++的维护(你可能想看看C++的信息标签,还有很多有趣的链接)。