派生类型的成员

时间:2017-06-28 21:46:16

标签: c++ memory abstract-class virtual

考虑以下示例:

  • 我有一个抽象类socket,它为一个很好的面向对象的套接字定义了一些虚拟接口(例如,sendreceive)。
  • 然后该类以多种方式专门化(例如,在我的情况下,我可以有TCP套接字或UDT套接字,它们以不同的方式工作(UDT是例如,基于底层的UDP套接字,但它们具有相同的接口,即一个可以是sendreceive
  • 我有一个connection类,它应该以某种方式包装一个套接字,添加了许多功能(例如发送和接收对象的模板方法,它序列化它们,一切都很漂亮)。

现在,我在connection中可以做的最简单的事情就是拥有一个socket *指针。然后connection可以在其构造函数中接受一个指针,一切都很好。但是,存在一个性能问题:只要我的connection对象想要与我的socket对象通信,就会出现需要时间的解除引用。

我想要的是让socket成为connection的成员。不要误解我的意思:我知道,如果没有别的,socket的不同专业可以有不同的大小,所以我不能简单地做一些事情:socket my_pretty_socket;

我的想法

我想知道的是,如果以下想法没有任何意义,以及原因。

我有socket的finte和有限的可能专精。如果我构建了一些template any <typename base, typename... specializations>

,该怎么办?
  • 分配一组尽可能多的char s作为最大的专业化。
  • 提供模板构造函数,将其参数转发给指定特化的构造函数,并在分配的数组上使用placement new来创建对象。
  • 表现得像socket的引用,例如通过提供operator * ()或将每个调用转发到socket的接口到它正在存储的专门化。

通过这种方式,我可以将对象作为connection的成员,并且在保存内存事务时保留接口的所有优良属性。

  1. 这个想法是否愚蠢?为什么?
  2. 是否已经有解决方案可以做到这一点?我相信我不可能是唯一一个遇到这种问题的人。

1 个答案:

答案 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,只要你可以使用提升)。