【问题标题】:Is there a way to add references to C++ class members to a vector without explicitly adding them?有没有办法将 C++ 类成员的引用添加到向量而不显式添加它们?
【发布时间】:2019-03-10 12:44:38
【问题描述】:

我正在尝试确定可以使用哪种模式以更“组合”和更少“命令”的方式声明 C++ 类。

我正在尝试编写一个包含多个 MyDescriptor 成员的类。 MyDescriptor 需要由 MyHost 类构造函数初始化,它会遍历对 MyHost 成员的引用来执行此操作。

有没有办法声明 MyHost 实现不需要单独添加对容器的类成员引用?

class MyDescriptor {
public:
  string name;
  string fqn;
  MyHost(string n) : name(n) {}
  void init(string host) {
    fqn = host + ":" + name;
  }
  void do_thing() {
    // subclasses to special things.
  }

}

class MyHost {
public:
  vector<MyDescriptor*> descriptors;
  string name
  MyHost(string n, vector<MyDescriptor*> d) : name(n),descriptors(d) {
    for (MyDescriptor *d : descriptors) {
      d->init(name);
    }
  }
}

MyHostImpl : public MyHost {
public:
  // descriptors must be accessible as members like this
  MyDescriptor d_1 = MyDescriptor("abc");
  MyDescriptor d_2 = MyDescriptor("123");

  MyHostImpl(string n) : MyHost(n, {&d_1, &d_2}) {} // This is the issue

  void do_thing_1() {
    // UPDATE: This is loose example - but what is important to
    // know is that developers need to access / use descriptors
    // in methods like this.
    d_1.do_thing();
  }

}

理想情况下,我希望有一种方法可以停止明确声明 descriptors 项目;这个{&amp;d_1, &amp;d_2} 是我想要消除的。我的团队使用了类似的模式,并且在将向量添加到类后不小心没有将描述符添加到向量中,因此经常感到沮丧。

【问题讨论】:

  • 为了确保我理解:出于某种目的,您需要保留descriptors 成员(或等效项),是吗?所以问题是如何初始化它,或者如何实现不需要显式初始化的替换?
  • 这可能有助于展示如何使用descriptors 的代表性示例。
  • 查找std::initializer_list
  • @JohnBollinger 是的,你是对的。 descriptors 需要是班级成员。它们目前以惰性方式初始化,并且在内部/外部的类中使用很多。 hosts 本质上只是对descriptors 组具有特定抽象的包装类。描述符仍被声明为成员并以某种方式初始化的替代实现将是一个理想的解决方案。优选的MyHost 实现不必进行显式初始化。我会更新示例...

标签: c++ class composition


【解决方案1】:

反转 ctor 语义。不是宿主 ctor 接受描述符指针,而是让描述符的 ctor 获取宿主引用并将自身添加到宿主向量:

    MyDescriptor::MyDescriptor(MyDescriptor const&)=delete;
    auto& MyDescriptor::operator=(MyDescriptor const&)=delete;
    MyDescriptor::MyDescriptor(string n, MyHost& h): name{n},fqn{n+":"+h.get_name()}
    {
        h.add(this);
    };

    MyHost::MyHost(string n):name{n}{};
    void MyHost::add(MyDescriptor* d){
        descriptors.push_back(d);
    };
    auto& MyHost::get_name()const{
        return name;
    };

这样你就不会忘记给宿主添加描述符或者编译器会哭。

MyHostImpl::MyHostImpl(string n):
    MyHost{n},
    d_1{"abc",*this},/*compiler kills you if you forget this line*/
    d_2{"123",*this}/*compiler kills you if you forget this line*/
{};

【讨论】:

  • 这不能满足需要的是 a class that includes multiple MyDescriptor members 。要清楚descriptors 必须是会员。
  • @AageTorleif 我没有在帖子中包含“MyHostImpl”。只需尝试定义该类,您就会发现它很合适。它不会神奇地为描述符成员创建初始化程序,而是让您避免忘记初始化主题。第二个想法,我要将“MyHostImpl”的 ctor 添加到帖子中。
  • 哦!这实际上很不错。我不喜欢需要 host 类型的 descriptor 类型,但我认为这可以通过一点类型重构来实现。
  • @AageTorleif 我也不喜欢这段代码,但它是一些遗留代码的快速补丁。我在 QT 小部件库之后模仿它。但请保持头脑开放。??
【解决方案2】:

是的,至少在 C++14 中,有一种方法可以直接执行您的要求,但它涉及到一堆黑暗巫术。在本次会议 C++ 2018 演讲中:

Better C++14 reflections - Antony Polukhin

Antony 解释了如何遍历结构的所有成员并访问其类型。他使用它在某些输出流上打印值,但您可以使用相同的方法将地址推回std::vector&lt;MyDescriptor*&gt;

话虽如此 - 我认为您应该避免这种模式。只需创建一个 MyDescriptors 向量开始,并使用对它的引用。为什么要使用需要大量扭曲才能产生的指针向量?

【讨论】:

  • “为什么要使用需要如此多的扭曲才能产生的指针向量?”我认为这是为了让一些遗留代码更简单。它可能已经做到了——但是当我们不遵循样板文件(初始化向量)时,很难追踪问题。有时我们在host 上有几十个descriptors,而不必显式定义向量会更容易。
  • “我认为这是为了让一些遗留代码更简单。”
【解决方案3】:

我不确定你会如何想象它会起作用。 C++ 没有反射,因此您无法遍历它的类成员并检查它们的类型。所以,对不起,据我所知不可能。您当然可以使用一些变通方法,例如直接使用底层基向量而不是将两个描述符声明为成员变量(您有什么需要这样做的理由吗?),然后在 Base 构造函数中隐式传递它们,但是你'重新瞄准是不可能的。

编辑:显然可以使用一些黑暗的巫术魔法,请参阅下面的评论。

【讨论】:

  • 实际上,如果你正确地打牌,C++ 会有相当多的反思,你可以肯定遍历一个普通结构的数据成员并检查它们的类型。看我的回答。当然 - 如果它是一个具有私有/受保护成员、非默认构造函数等的类,那么你不能。
  • 这并不是说我想遍历成员本身 - 将对象分配给类成员并在向量中引用它的更好方法是真正需要的。更好的方式是指一种易于声明且难以出错的方式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多