简体   繁体   English

Base class 持有对 Derived 的引用

[英]Base class holding a reference to Derived

I'd like to do this:我想这样做:

struct Derived;

struct Base{
    Derived const& m_ref;
    Base(Derived const& ref) : m_ref(ref){}
};

struct Derived: Base{
    Derived(): Base(*this){}
};

But I seem to get unreliable behaviour (when used later on, m_ref points to things that aren't valid Derived).但我似乎得到了不可靠的行为(稍后使用时, m_ref指向无效的衍生事物)。

Is it permissible to construct a reference to Derived from *this before the class has been initialised?是否允许在 class 初始化之前构造对 Derived from *this的引用?

I appreciate that it is not valid to use such a reference until it has been initialised, but I don't see how changes to the initialisation of a class can affect references to it (since initialising it doesn't move it around in memory...).我明白在初始化之前使用这样的引用是无效的,但我看不出对 class 的初始化的更改如何影响对它的引用(因为初始化它不会在 memory 中移动它。 ..)。

I'm not sure what to call what I'm trying to do, so my search for information on this has drawn a blank...我不知道该怎么称呼我正在尝试做的事情,所以我搜索这方面的信息是一片空白......



Update: I can't reproduce my problems with a simple test case, so it looks like it is probably okay (though I can't prove it, and would still welcome a definitive answer).更新:我无法用一个简单的测试用例重现我的问题,所以看起来可能没问题(尽管我无法证明这一点,但仍然欢迎明确的答案)。 Suspect my problems arose from a broken copy-assignment operator.怀疑我的问题是由损坏的复制分配运算符引起的。 That's another matter altogether though!这完全是另一回事!

Update 2 My copy constructor and copy-assignment operators were indeed to blame, and now this seems to work reliably.更新 2我的复制构造函数和复制赋值运算符确实是罪魁祸首,现在这似乎工作可靠。 Still interested in whether or not it is well-defined behaviour though.不过,仍然对它是否是明确定义的行为感兴趣。

I think in general you're OK doing this, but be very careful in constructors and destructors.我认为总的来说你可以这样做,但在构造函数和析构函数中要非常小心。 In particular, in Base::~Base , the Derived part of the object has already been destroyed so don't use m_ref there.特别是在Base::~Base中,object 的Derived部分已经被销毁,所以不要在那里使用m_ref

3.8/6 says what you can do with a pointer/reference to memory for an object that has been allocated but not yet constructed. 3.8/6 说明了对于已分配但尚未构建的 object 的指针/引用,您可以做什么。 Your code doesn't provoke an lvalue-to-rvalue conversion, or otherwise break the rules, so I'd think that you're fine.您的代码不会引发左值到右值的转换,或者以其他方式违反规则,所以我认为您很好。 Since you're observing bad values, though, I may well have missed something.但是,由于您观察到了错误的价值观,因此我很可能错过了一些东西。 Or your code might be otherwise bad.否则您的代码可能会很糟糕。

Even if you did break those rules, 12.6.2 and 12.7 list additional things that you can do during construction and destruction.即使您确实违反了这些规则,12.6.2 和 12.7 也会列出您在构建和销毁期间可以做的其他事情。

Edit: ah, 8.3.2/4: "A reference shall be initialized to refer to a valid object or function."编辑:啊,8.3.2/4:“应初始化引用以引用有效的 object 或 function。” You initialize m_ref to refer to an object whose constructor hasn't even been entered yet.您初始化m_ref以引用 object,其构造函数尚未输入。 I don't know without further research whether an object under construction is "valid" or not, and in particular whether the object of most-derived type is "valid" at the time of construction of the base class.如果没有进一步研究,我不知道正在构建的 object 是否“有效”,尤其是在构建基础 ZA2F2ED4F8EBC2CBB4C21A29DC40AB6 时,最衍生类型的 object 是否“有效”。 This could perhaps be the problem, though.不过,这可能是问题所在。

You might think that no unconstructed object is "valid", but then this would be invalid:您可能认为没有未构造的 object 是“有效的”,但这将是无效的:

class Foo {
    Foo() {
        Foo &self = *this; // reference initialized to refer to unconstructed object!
    }
};

So, is that invalid?那么,这是无效的吗? If not, does the most-derived object become valid somewhere between the start of the base class constructor call and the start of the derived class constructor call?如果不是,那么最派生的 object 在基础 class 构造函数调用的开始和派生的 class 构造函数调用的开始之间的某处是否有效? I dunno, sorry.我不知道,对不起。

3.8/1 says: 3.8/1 说:

The lifetime of an object of type T begins when : — storage with the proper alignment and size for type T is obtained, and — if T is a class type with a non-trivial constructor (12.1), the constructor call has completed .类型 T 的 object 的生命周期开始于: — 存储具有适当的 alignment 和类型 T 的大小,并且 — 如果 T 是 classial 类型,则构造函数调用已完成构造函数。12。

3.8/5 says: 3.8/5 说:

Before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that refers to the storage location where the object will be or was located may be used but only in limited ways . Before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that refers to the storage可以使用 object 将位于或曾经位于的位置,但只能以有限的方式使用 Such a pointer refers to allocated storage (3.7.3.2), and using the pointer as if the pointer were of type void*, is well-defined.这样的指针指的是分配的存储(3.7.3.2),并且使用指针就像指针是 void* 类型一样,是明确定义的。 Such a pointer may be dereferenced but the resulting lvalue may only be used in limited ways, as described below.这样的指针可能会被取消引用,但生成的左值只能以有限的方式使用,如下所述。

"Below" is 3.8/6: “以下”是 3.8/6:

Such an lvalue refers to allocated storage (3.7.3.2), and using the properties of the lvalue which do not depend on its value is well-defined.这样的左值指的是分配的存储(3.7.3.2),并且使用不依赖于其值的左值的属性是明确定义的。

...and then a list of things you can't do. ...然后列出你不能做的事情。 Binding to a reference to the same, derived type is not among them.绑定到对相同的派生类型的引用不在其中。

I can't find anything elsewhere that might make your code invalid.我在其他地方找不到任何可能使您的代码无效的东西。 Notably, despite the following phrase in 8.3.2/4:值得注意的是,尽管 8.3.2/4 中有以下短语:

A reference shall be initialized to refer to a valid object or function.应初始化引用以引用有效的 object 或 function。

there doesn't seem to be any definition of "valid object" to speak of.似乎没有任何“有效对象”的定义可言。

So, after much to-ing and fro-ing, I must conclude that it is legal .所以,经过多次来回,我必须得出结论,这是合法的


Of course, that's not to say that it's in any way a good idea!当然,这并不是说它在任何方面都是一个好主意! It still looks like a bad design.它看起来仍然是一个糟糕的设计。

For example, if you later change your base constructor and any of the following become relevant (again from 3.8/6):例如,如果您稍后更改基本构造函数并且以下任何内容变得相关(再次从 3.8/6 开始):

  • the lvalue is used to access a non-static data member or call a non-static member function of the object左值用于访问非静态数据成员或调用 object 的非静态成员 function
  • the lvalue is implicitly converted (4.10) to a reference to a base class type左值被隐式转换(4.10)为对基本 class 类型的引用
  • the lvalue is used as the operand of a static_cast (5.2.9) (except when the conversion is ultimately to char& or unsigned char&左值用作static_cast (5.2.9) 的操作数(除非最终转换为char&unsigned char&
  • the lvalue is used as the operand of a dynamic_cast (5.2.7) or as the operand of typeid .左值用作dynamic_cast (5.2.7) 的操作数或typeid的操作数。

...then your program will be undefined, and the compiler may emit no diagnostic for this! ...那么您的程序将是未定义的,编译器可能不会为此发出任何诊断!


Random other observations随机其他观察

I notice a couple of other interesting things whilst compiling this answer, and this is as good a place as any to share them.在编译这个答案时,我注意到了其他一些有趣的事情,这是一个分享它们的好地方。

First, 9.3.2 appears to leave the type of this in a ctor-initializer accidentally unspecified.首先,9.3.2 似乎在ctor-initializer中意外地未指定this的类型。 Bizarre!奇怪!

Second, the criteria set on a pointer by 3.8/5 (not the same list that I quoted from 3.8/6) include:其次,3.8/5 对指针设置的标准(与我从 3.8/6 引用的列表不同)包括:

If the object will be or was of a non-POD class type, the program has undefined behavior if [..] the pointer is implicitly converted (4.10) to a pointer to a base class type.如果 object 将是或曾经是非 POD class 类型,则如果 [..] 指针被隐式转换 (4.10) 为指向基本 ZA2F2ED4F8EBC2CBB4C21A21DZ40AB6 类型的指针,则程序具有未定义的行为。

I believe that this renders the following innocuous-looking code undefined:我相信这会使以下看起来无害的代码未定义:

struct A {
   A(A* ptr) {}
};

struct B : A {
   B() : A(this) {}
};

int main() {
   B b;
}

I think the big problem in this is that you think you want to do one thing, when in reality you actually want to do something else.我认为最大的问题是你认为你想做一件事,而实际上你真的想做别的事情。 Strange, huh?奇怪吧?

Is it permissible to construct a reference to Derived from *this before the class has been initialised?是否允许在 class 初始化之前构造对 Derived from *this 的引用?

Yes, as long as you don't use it (for anything but storing a reference to it) in the scope of the Base constructor and remember in ~Base that Derived is destroyed before Base.是的,只要你不在 Base 构造函数的 scope 中使用它(除了存储对它的引用之外的任何东西),并记住在 ~Base 中 Derived 在 Base 之前被销毁。

But why on earth do you think that Base wants to know of Derived?但是你到底为什么认为 Base 想知道 Derived 呢? If it's static polymorphism you are after, then the curiously recurring template pattern is what you want:如果您追求的是 static 多态性,那么奇怪的重复模板模式就是您想要的:

template <typename T>
class Base {};

class Derived : public Base<Derived> {};

But I don't really think that's what you're aiming at.但我真的不认为这就是你的目标。

Maybe you want a way for Base to communicate with a client and think that should be done with inheritance?也许您想要 Base 与客户端通信的方式,并认为应该使用 inheritance 来完成? If so, then this observer-ish idiom is what you need:如果是这样,那么这个观察者式的成语就是你所需要的:

class Client
{
public: 
    virtual void Behavior() = 0;

protected:
    ~Client() {}
};

class Base
{
    Client& client_;

public:
    Base(Client& client) : client_(client) {}
};

class Implementor : public Client
{
public:
    Implementor() : Base(*this) {}

    virtual void Behavior() { ... }
};

If not even that is what you want, then you need to rethink your design.如果这不是您想要的,那么您需要重新考虑您的设计。

I'm actually implementing a generic base class that takes a template parameter class and derives from it, and adds a "safe bool" conversion based on the result of a function call on the derived type.我实际上正在实现一个通用的基础 class ,它采用模板参数 class 并从中派生,并基于对派生类型的 function 调用的结果添加“安全布尔”转换。 I'd like to avoid using virtual functions, if possible, because I'm a serial premature optimiser I really do care about performance in some of the places I'd like to use this.如果可能的话,我想避免使用虚函数,因为我是一个过早的串行优化器,我真的很关心我想使用它的一些地方的性能。 – Autopulated 37 mins ago – 37 分钟前自动填充

You don't need a reference to the Derived class.您不需要引用派生的 class。 Your class is deriving from a template parameter.您的 class 源自模板参数。 Just use the common method.只需使用常用方法即可。

#include <iostream>

template <class T>
class Base : public T
{
public:
    bool operator!() const 
    {
        return !this->isOk();
    }
};

class TemplateClass
{

public:
    bool isOk() const
    {
        return true;
    }
};

int main (int argc, char* argv[])
{
    Base<TemplateClass> myClass;

    if (!!myClass)
    {
        std::cout << "ok" << std::endl;
    }
    else
    {
        std::cout << "not ok" << std::endl;
    }

    return 0;
}

You can even use template specialization if you know ahead of time of derived classes that don't implement a common bool check.如果您提前知道没有实现通用布尔检查的派生类,您甚至可以使用模板特化。

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

相关问题 将基类的指针转换为派生类(参考) - Casting base class pointer to derived class (reference) 在对基类的引用中存储派生类的对象 - Storing an object of a derived class in a reference to the base class 将Derived类指针转换为Base类引用 - Casting a Derived class pointer to a Base class reference 基类对派生类的指针的引用 - Base Class reference to Pointer of Derived Class 基本 class 参考派生 object - 策略模式 - base class reference to derived object - strategy pattern 从(对基础对象的基础引用)到(派生类引用)的静态转换 - Static Cast from ( Base Reference to Base Object ) to ( Derived Class Reference) 将派生对象作为基类参考参数传递 - passing derived object as base class reference parameter 保留派生类引用的基类的std :: unique_ptr在gcc编译器中不显示警告,而裸指针显示它。 为什么? - std::unique_ptr of base class holding reference of derived class does not show warning in gcc compiler while naked pointer shows it. Why? 从抽象基础 class 引用转换为派生 class 引用 - Casting from an abstract base class reference to derived class reference 我可以使用对派生类实例的基类引用来初始化派生类引用吗? - Can I Initialize a derived class reference with a base class reference to derived class instance?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM