Itamar Ivri
Itamar Ivri

Reputation: 117

Initializing a class object as wrong type

I have this code and I'd like to ask - how is it possible to initialize a class object with new int() without getting an exception? When I print the value of the object it prints junk but I still don't understand how this assignment is possible. What exactly happens when you use new int(value)? doesn't it create space for an int and initializes it with value?

class A {
    static A* first;
    static A* last;
    A *prev, *next;
public:
    A() : prev(last), next(nullptr) {
        cout << "A ctor" << endl;
        if (last) last->next = this;
        last = this;
        if (!first) first = this;
    }
    virtual ~A() {
        if (prev) prev->next = next;
        if (next) next->prev = prev;
        if (first == this) first = next;
        if (last == this) last = prev;
    }
    static void printAll() {
        if (first) cout << *first;
        else cout << "list is empty" << endl;
    }
    friend ostream& operator<<(ostream& out, const A&a) {
        a.printSelf();
        if (a.next) out << *(a.next);
        else out << endl;
        return out;
    }
    virtual void printSelf() const = 0;
};

template<class T>
class B : public A {
    T * t;
public:
    B(const T& _t) : t(new T(_t)) { cout << "B ctor" << endl; }
    virtual ~B() {
        cout << "Dtor B" << endl;
        cout << *this;
        delete t;
    }
    virtual void printSelf()const override {
        cout << *t << ' ';
    }
};
A* A::first = nullptr;
A * A::last = nullptr;

int main() {

    B<int*>b1 = new int(5);
    b1.printSelf(); //prints junk (b1.t value)
    cout << endl;
    cout << typeid(b1).name() << endl; //prints class B<int*>
}

Upvotes: 1

Views: 181

Answers (1)

Adrian Maire
Adrian Maire

Reputation: 14865

You are probably looking for explicit keyword:

Without explicit, the compiler will try implicit conversion between types using the constructor:

class Test
{
public:
Test(int){}
};

Test t = 42;

If this is not supposed to happens, then you can use explicit (I personally recommend to use it by default on each non-copy single-argument constructor.

class Test
{
public:
explicit Test(int){}
};

Test t = 42; //error here

In your case, T is a pointer to int, thus, new int(5) will generate an integer on heap and return a pointer to it. This is a valid argument for B.

Looking inside your constructor:

B(const T& _t) : t(new T(_t)) { cout << "B ctor" << endl; }

your receive a _t which is a pointer to int (e.g. 0xabcd -> 5), and you initialize this->twith a new pointer to pointer to int, initialized with 0xabcd. That is:

B(const int*& _t): t(new int*(_t))...
// t is &(0xabcd), *t is 0xabcd, **t is 5

Then printing *t:

cout << 0xabcd << ' '; // printing the memory address, not the value 5.

You can try this instead:

cout << **t << ' '; // Print 5

But... it's probably better to solve the double allocation and remove the memory leak:

B<int>b1 = 5;

Upvotes: 3

Related Questions