开发者

C++ constructors fun - constructing Foo with a copy of itself

开发者 https://www.devze.com 2022-12-21 07:40 出处:网络
I have: class Foo; class Bar { Foo foo; Bar(): foo(foo) {}; } Bar bar; At this point, is bar.foo // <--- how is this initialized?

I have:

class Foo;

class Bar {
  Foo foo;
  Bar(): foo(foo) {};
}

Bar bar;

At this point, is

bar.foo // <--- how is this initialized?

[This question arose from a buggy ref-counted pointer implemntation; I could have sworn tha开发者_StackOverflow中文版t I ensured each pointer was pointing at something non-null; but I ended up with a pointer that pointed at something NULL.]


foo is fully initialized once you've entered the body of the constructor (that's the guaranteed general case; specifically once it has finished initializing in the initialize list.)

In your case, you are copy-constructing from a non-constructed object. This results in undefined behavior, per §12.7/1 (thank you, gf):

For an object of non-POD class type (clause 9), before the constructor begins execution and after the destructor finishes execution, referring to any nonstatic member or base class of the object results in undefined behavior.

In fact, it gives this example:

struct W { int j; };
struct X : public virtual W { };
struct Y {
    int *p;
    X x;
    Y() : p(&x.j) // undefined, x is not yet constructed
    { }
};

Note, the compiler is not required to give a diagnosis of undefined behavior, per §1.4/1. While I think we all agree it would be nice, it simply isn't something the compiler implementers need to worry about.


Charles points out a loophole of sorts. If Bar has static storage and if Foo is a POD type, then it will be initialized when this code runs. Static-stored variables are zero-initialized before an other initialization runs.

This means whatever Foo is, as long as it doesn't need a constructor to be run to be initialized (i.e., be POD) it's members will be zero-initialized. Essentially, you'll be copying a zero-initialized object.

In general though, such code is to be avoided. :)


Bar(): foo(foo) {};

This will call the copy constructor of foo, thus copy-constructing from a non-initialized object. That will result in undefined behavior, except if you have implemented a copy constructor that handles that specific case, for example:

class Foo
{
    public:
        Foo()
        {
            std::cout << "Foo()";
        }

        Foo(const Foo& from)
        {
            if(this == &from) std::cout << "special case";
            else std::cout << "other case"; 
        }
};

But that special case is normally used for other purposes, like cheap copies of strings (when using a string class). So don't try and exploit that special case ;)


A slightly expanded version of your code seems to indicate that no, foo is never initialized; you would seem to have undefined behavior. In this example, "Foo()" is never printed, indicating no instance of Foo is ever constructed:

#include <iostream>

class Foo {
public:
    Foo() { std::cerr << "Foo()"; }
};

class Bar {
public:
    Foo foo;
    Bar(): foo(foo) {};
};

int main() {
    Bar bar;
}


Isn't Foo using a default intrinsic constructor, with the initialisation list invoking that default constructor automatically to initialise the object?

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号