考虑以下示例:
socket
,它为一个很好的面向对象的套接字定义了一些虚拟接口(例如,send
和receive
)。TCP
套接字或UDT
套接字,它们以不同的方式工作(UDT
是例如,基于底层的UDP
套接字,但它们具有相同的接口,即一个可以是send
和receive
。connection
类,它应该以某种方式包装一个套接字,添加了许多功能(例如发送和接收对象的模板方法,它序列化它们,一切都很漂亮)。现在,我在connection
中可以做的最简单的事情就是拥有一个socket *
指针。然后connection
可以在其构造函数中接受一个指针,一切都很好。但是,存在一个性能问题:只要我的connection
对象想要与我的socket
对象通信,就会出现需要时间的解除引用。
我想要的是让socket
成为connection
的成员。不要误解我的意思:我知道,如果没有别的,socket
的不同专业可以有不同的大小,所以我不能简单地做一些事情:socket my_pretty_socket;
我想知道的是,如果以下想法没有任何意义,以及原因。
我有socket
的finte和有限的可能专精。如果我构建了一些template any <typename base, typename... specializations>
:
char
s作为最大的专业化。new
来创建对象。socket
的引用,例如通过提供operator * ()
或将每个调用转发到socket
的接口到它正在存储的专门化。通过这种方式,我可以将对象作为connection
的成员,并且在保存内存事务时保留接口的所有优良属性。
答案 0 :(得分:1)
根据问题+评论,我认为最好的办法是简单地删除继承并使用variant
。该解决方案需要boost,C ++ 17或具有变体TS的实现的新编译器/标准库,并且优选地为14.变体是存储若干类型之一的类型。它根据最大类型的大小将所有内容存储在堆栈中。
struct tcp {
void do_stuff() const;
};
struct udp {
void do_stuff() const;
};
using socket_type = std::variant<tcp,udp>;
void do_stuff(const socket_type& s) {
visit([] (const auto& x) { x.do_stuff(); }, s);
}
enum class SocketKind { TCP, UDP };
template <class ... Args>
socket_type make_socket(SocketKind k, Args && args) {
if (k == SocketKind::TCP) { return tcp(std::forward<Args>(args)...); }
if (k == SocketKind::UDP) { return udp(std::forward<Args>(args)...); }
}
您可以使用这样的工厂创建这些套接字,然后您可以在套接字对象上一般调用do_stuff
。所有东西都存储在内存中,并且调度将通过switch-case处理,如果你有少量套接字类型,这可能比vtable更快(但这只是猜测)。
(请注意,我使用variadic lambda来编写do_stuff
,这只是简化了事情,但并不是真的需要。你可以手工写出访问者,所以你仍然可以使用这个解决方案甚至C + +03,只要你可以使用提升)。