是否必须初始化成员才能使用其地址?

时间:2019-05-10 13:43:58

标签: c++ pointers initialization language-lawyer

我可以在初始化数据成员之前初始化指向该数据成员的指针吗?换句话说,这是有效的C ++吗?

#include <string>

class Klass {
public:
    Klass()
        : ptr_str{&str}
        , str{}
    {}
private:
    std::string *ptr_str;
    std::string str;
};

this问题与我的相似,但是那里的顺序是正确的,答案是

  

我建议不要像这样编码,以防有人更改您班上成员的顺序。

这似乎意味着撤销订单是非法的,但我不确定。

2 个答案:

答案 0 :(得分:25)

  

是否必须初始化成员才能使用其地址?

否。

  

我可以在初始化数据成员之前初始化指向该数据成员的指针吗?换句话说,这是有效的C ++吗?

是的。是的。

对一元&操作数的初始化没有限制。标准中的一元&运算符规范中有一个示例:

int a;
int* p1 = &a;

在这里,a的值未初始化,可以指向它。

该示例未演示的是在其生命周期开始之前指向一个对象,这就是您的示例中发生的情况。如果存储被占用,则显式允许使用指向对象生命周期前后的指针。标准草案说:

  

[basic.life]在对象的生存期开始之前但已分配了该对象将占用的存储空间之后,或者在对象的生存期结束之后以及该对象占用的存储空间被重用或释放之前,可以使用任何表示对象将要或曾经位于的存储位置的地址的指针,但只能使用有限的方式...

该规则继续列出如何限制使用。您可以按常识相处。简而言之,您可以像对待void*一样对待它,除非违反这些限制是UB而不是格式错误。有类似的规则可供参考。

专门计算非静态成员的地址也受到限制。标准草案说:

  

[class.cdtor] ...要形成指向对象obj的直接非静态成员的指针(或访问其值),obj的构造应已开始,并且它的销毁尚未完成,否则指针值的计算(或访问成员值)将导致不确定的行为。

Klass的构造函数中,Klass的构造已经开始并且破坏还没有完成,因此可以满足上述规则。


P.S。您的类是可复制的,但是副本将具有指向另一个实例的成员的指针。考虑一下这是否对您的课堂有意义。如果没有,则需要实现自定义复制并移动构造函数和赋值运算符。像这样的自引用是一种罕见的情况,在这种情况下,您可能需要针对它们的自定义定义,而不需要自定义析构函数,因此这是五个(或三个)规则的例外。

P.P.S如果您的目的是指向成员之一,并且没有对象指向成员,那么您可能想使用指向成员的指针而不是指向对象的指针。

答案 1 :(得分:-1)

有趣的问题。

这是合法的并且可以“起作用”,尽管勉强可以。有一些与类型有关的“但是”,这会使整个事情变得有点尴尬,但味道不好(但不是非法的),并且可能使某些涉及继承的边境案件成为非法。

当然,您可以获取任何对象的地址,无论该对象是否已初始化,只要它存在于作用域中并且具有可以在operator&之前添加的名称即可。取消引用指针是另一回事,但这不是问题。

现在,一个微妙的问题是the standard将非静态结构成员的operator&的结果定义为”“指向类型T的C类成员的指针”,并且是prvalue指定C :: m“

这基本上意味着ptr_str{&str} 使用地址str,但是类型不是 pointer-to ,而是指向成员的指针。然后将其隐式和静默转换为 pointer-to

换句话说,尽管您不需要显式编写&this->str,但这仍然是它的类型-它是什么,它的意思是 [1]

>

this是否有效,并且在初始化列表中使用它是否安全?好吧,只是... 勉强。使用as long as it's not being used to access uninitialized members or virtual functions, directly or indirectly是安全的。碰巧的是这里的情况(在另一个可以争论的情况下可能不是这种情况。)


[1] 有趣的是,第4段以一个子句开头,该子句表示将内容放在括号中时不会形成成员指针。这很了不起,因为大多数人可能会这样做,只是为了100%确保他们正确设置了操作员优先级。但是,如果我没看错,那么&this->foo&(this->foo)根本就不一样!