Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delegating constructor gives segmentation fault when using class field for argument

Actually the segmentation fault happens in another program I tried to compile which happens because of this behaviour.

My question is:

This is a bug or my fault?

Reproducible in any way (even if the something field is private or protected) and here is my example:

main.cc:

#include <iostream>
class Test {
    public:
        const char* something = "SOMETHING HERE!!!";
        Test(const int& number) : Test(something, number) { }
        // XXX: changed `something` to `_something` to make it different
        Test(const char* _something, const int& number) {
            std::cout << _something << std::endl;
            std::cout << number << std::endl; }
        ~Test() { }
};

int main(int argc, char* argv[]) {
    Test te1(345);
    Test te2("asdasdad", 34523);
    return 0;
}

And here is what happens when compiling with:

g++ main.cc -Os -o main

and running with:

./main

the output is:

pi@pi:~/ $ ./main
A"�~ <-- this is random
345
asdasdad
34523

But when I enable optimization with -O0 or -O1 or -O2 ... the output is only a new line:

pi@pi:~/ $ ./main
pi@pi:~/ $

G++ version:

pi@pi:~/ $ g++ --version
g++ (Raspbian 6.3.0-18+rpi1) 6.3.0 20170516
like image 542
Memos Electron Avatar asked Dec 24 '17 17:12

Memos Electron


1 Answers

const char* something = "SOMETHING HERE!!!";

The default-initializer on the right is, as its name implies, only used when you don't provide an explicit initializer in a constructor's initializer list. Let's look at yours:

Test(const int& number) : Test(something, number) { }

Okay, we're delegating to another constructor. That other constructor will perform full initialization, so the default initializer is not used. But... we're passing in the uninitialized value of something as parameter.

Test(const char* _something, const int& number) { /* ... */ }

Uh-oh. Now we're trying to use the value of _something, which is a copy of something, which is indeterminate. Undefined Behaviour and fire ensue.

You really shouldn't pass the value of a class member as parameter to its constructor, unless you have an infinite supply of fireproof chickens and eggs.


The behaviour you're looking for can be obtained by putting the default value in the call to the delegate constructor:

Test(const int& number) : Test("SOMETHING HERE!!!", number) { }

... or keeping it in a dedicated static variable:

static constexpr char *const defaultSomething = "SOMETHING HERE!!!";
Test(const int& number) : Test(defaultSomething, number) { }
like image 199
Quentin Avatar answered Oct 29 '22 09:10

Quentin