简体   繁体   English

正在传递(在构造函数中)指向包含该类的类的类的指针,该类的设计不良,解决方案是什么

[英]Is passing(in constructor) a pointer to class that contains it a bad design and if so what is the solution

often I encounter code like 我经常遇到类似的代码

/*initializer list of some class*/:m_member(some_param,/* --> */ *this)

Reason why this is done is so that m_member can call member functions from the class that contains it... aka 之所以这样做,是因为m_member可以从包含它的类中调用成员函数。

//code in class that is m_member instance of

    m_parent->some_function();

I personally dislike it because I consider it pathetic design("dear child do you know what are you doing to your class encapsulation"), but I would like to know is in general this behavior bad, and if so how to avoid this kind of design. 我个人不喜欢它,因为我认为它是可悲的设计(“亲爱的孩子,您知道您对类的封装做了什么”),但是我想知道这种行为总体上是不好的,如果可以的话,如何避免这种行为设计。

EDIT: please dont focus on this in initalizer list, lets say it is in ctor body. 编辑:请不要在初始化列表中专注于此,可以说它在ctor主体中。

It is bad because it is unclear how complete the parent class is at the time m_member is constructed. 不好是因为不清楚在构造m_member时父类的完成程度。

For example: 例如:

class Parent
{
   Parent()
   : m_member(this), m_other(foo)
   { }
};

class Member
{
    Member(Parent* parent)
    {
       std::cout << parent->m_other << std::endl; // What should this print?
    }
};

A slightly better approach if a parent pointer is needed is for Member to have a 'setParent' method called in the body of the constructor. 如果需要父指针,则更好的方法是让Member在构造函数的主体中调用一个“ setParent”方法。

It can be disastrous, since your parent is not constructed a the time of the reference-set. 这可能是灾难性的,因为您的父母不是在参考时间建立的。 The following example will demonstrate this: 以下示例将说明这一点:

#include <iostream>
using namespace std;

struct TheParent;

struct TheChild
{
    TheChild(TheParent& parent);
    TheParent& myParent;
};

struct TheParent
{
    TheParent()
      : mychild(*this)
      , value(1)
    {
        cout << "TheParent::TheParent() : " << value << endl;
    }

    TheChild mychild;
    int value;
};

TheChild::TheChild(TheParent& parent)
   : myParent(parent)
{
    cout << "TheChild::TheChild() : " << myParent.value << endl;
};

int main()
{
    TheParent parent;
    return 0;
}

Produces the following output, clearly noting the indeterminate state of the parent object: 产生以下输出,清楚地指出父对象的不确定状态:

TheChild::TheChild() : 1606422622
TheParent::TheParent() : 1

Bottom line: don't do it this way. 底线:不要这样。 You would be better served to use a dynamic child allocation instead, but even this has caveats: 您最好使用动态子代分配,但是即使这样做也有一些警告:

#include <iostream>
using namespace std;

struct TheParent;

struct TheChild
{
    TheChild(TheParent& parent);
    TheParent& myParent;
};

struct TheParent
{
    TheParent()
      : mychild(NULL)
      , value(1)
    {
        mychild = new TheChild(*this);
        cout << "TheParent::TheParent() : " << value << endl;
    }

    ~TheParent()
    {
        delete mychild;
    }

    TheChild* mychild;
    int value;
};

TheChild::TheChild(TheParent& parent)
   : myParent(parent)
{
    cout << "TheChild::TheChild() : " << myParent.value << endl;
};


int main()
{
    TheParent parent;
    return 0;
}

This give you what you're likely hoping for: 这给您您可能希望的:

TheChild::TheChild() : 1
TheParent::TheParent() : 1

Note, however, even this has issues if TheParent is an intermediate class in an inheritance chain, and you're desiring to access potentially overridden virtual implementations of functions in derived classes that have yet to be constructed. 但是请注意,如果TheParent是继承链中的中间类,那么即使这样也会有问题,并且您希望访问尚未构造的派生类中的功能可能会被覆盖的虚拟实现。

Again, bottom line, if you find yourself doing this, you may want to think about why you need to in the first place. 同样,最重要的是,如果您发现自己这样做,则可能首先要考虑为什么需要这样做。

Like the vast majority of programming practices, it is impossible to say that it is bad in general (and if you do, you are a bad person and should be ashamed). 像绝大多数编程实践一样,不可能一概而论地认为它是不好 (如果这样做,则说明您是坏人,应该感到羞愧)。 I use this sometimes, but it is uncommon; 我有时会用到它,但这并不常见。 however, it is not a thing I would try to purposefully avoid by changing my class design. 但是,通过更改类设计,我不会设法避免这种情况。

Note how I used "I" a lot in the above paragraph, a sure sign this is a highly subjective issue. 请注意,我在上一段中经常使用“ I”,这肯定是一个主观问题。

I see the language as a tool to implement the solution for a given problem. 我将语言视为实现给定问题的解决方案的工具。 By design, C++ allows explicit uses of this and other OO languages don't. 根据设计,C ++允许明确使用this语言,而其他OO语言则不允许。 Thus, I look at language features as tools in my toolbox, and every so often there is a use to bring out one tool or another. 因此,我将语言功能视为工具箱中的工具,并且经常会使用一种工具或另一种工具。

However, and that's where coding style and practice comes in, I should know what I'm doing. 但是,这就是编码风格和实践的发源地,我应该知道自己在做什么。 I should know how to use my tools, and I should know the implications of their use. 我应该知道如何使用我的工具,并且应该知道使用它们的含义。 There is a defined order in which C++ initializes a new object, and as long as I work with this then I'm good. 有一个定义的顺序,C ++以此顺序初始化一个新对象,只要我使用它就可以了。 Unfortunately, some times people get lucky; 不幸的是,有时候人们会很幸运。 other times they create bugs that way. 有时他们以这种方式创建错误。 You need to know your tools and how to use them :-) 您需要了解您的工具以及如何使用它们:-)

To answer your question with my personal opinion: I try to avoid this particular construct, but on occasion I had to use it. 为了以我的个人观点回答您的问题:我尝试避免使用这种特殊的构造,但有时我不得不使用它。 Even pondering a class re-design wouldn't have avoided that. 即使考虑重新设计班级,也无法避免这一点。 And so I filed this occasion under, "Ah well, sometimes my design just can't be modeled in clean-clean straight OO, the dependencies between the classes are too tight and performance matters too much." 因此,我在这种情况下提出了这样的建议:“好吧,有时候我的设计无法以简洁的直接OO建模,类之间的依赖关系太紧密了,而性能也变得非常重要。”

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM