Samik Samik - 4 years ago 361
C++ Question

How to make union like class with non-trivial member work properly?

I'm facing two problems while trying to make union-like-class with members having non-trivial destructor work. A segmentation fault that I can't understand and a way to call non-trivial destructor conditionally that I can't figure out.



#include <iostream>
#include <string>

using namespace std;

class A {
struct B {
int u;
double v;
string w;
};
union C {
B x;
unsigned char y;
C(int u, double v, const string& w) {
x.w = w;// why does it segfault here?
x.u = u;
x.v = v;
}
C(unsigned char c) {
y = c;
}
//omitting this complains because string has non-trivial destructor
~C() {
// how to call this conditionally based on A::isChar value
//x.w.~string();
}
} c;
bool isChar;
public:
A(unsigned char f)
: isChar{true}, c(f) {}

A(int g, double h, const string& i)
: isChar{false}, c(g, h, i) {}

friend ostream& operator<<(ostream& os, const A& a) {
if(a.isChar)
return os << a.c.y;
return os << a.c.x.u << " " << a.c.x.v << " " << a.c.x.w;
}
};

int main() {
A a1(10, 20.0, "samik"), a2('v');
cout << a1 << " " << a2 << endl;
return 0;
}


I'm compiling this with
g++ -g -std=c++14 -pedantic union_class_test.cpp -o union_class_test.out
with gcc 5.3.1. How to correctly implement this?

Answer Source

The program segfaults on

x.w = w;// why does it segfault here?

Because x hasn't been constructed, and x.w contains garbage. You will need to make sure B's constructor is called first.

    C(int u, double v, const string& w) : x()
    {
        x.w = w;// why does it segfault here?
        x.u = u;
        x.v = v;
    }

And in order to destruct correctly, you'll need to do it in A's destructor. Leave C::~C empty, and add a destructor to A.

~A()
{
    if (!isChar)
    {
        c.x.w.~string();
    }
}

You will also need to be careful when copying A's, because sometimes the string's destructor will need to be called, and sometimes it won't. One way to do so would be to use placement new to overwrite this, and can be implemented like...

A& operator=(const A& o)
{
    if (this != &o)
    {
        this->~A();

        if (o.isChar)
        {
            new (this) A(o.c.y);
        }
        else
        {
            new (this) A(o.c.x.u, o.c.x.v, o.c.x.w);
        }
    }

    return *this;
}

And, moves will be implemented similarly. But I'll leave that as an exercise to the reader.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download