简体   繁体   中英

C++, constructor, destructor

As a general opening question, it can be stated as why some times destructor gets called in the middle of the code?! what are the different possible scenarios can call a destructor.? I would like to understand the similar effect in the following code

class complex
{
private:
    double re, im;
protected:

public:
    complex()
    {
        cout << "def const  " << endl;
    }

    complex(double r, double i)
    {
        cout << "parameterized " << endl;
        re = r;
        im = i;
    }

    void setdata(double r, double i)
    {
        re = r;
        im = i;
    }

    void getdata()
    {
        cout << "enter real" << endl;
        cin >> re;
        cout << "enter im" << endl;
        cin >> im;
    }

    //there are 3(?) possible variants of addition...check
    //1st
    void add(complex c1, complex c2)
    {
        cout << "in add" << endl;
        re = c1.re + c2.re;
        im = c1.im + c2.im;
    }

    //2nd
    void add(complex c)
    {
        cout << "in add" << endl;
        re += c.re;
        im += c.im;
    }

    //3rd --- (???)
//    complex add(complex c1, complex c2)
//    {
//        complex retc;
//        retc.re = c1.re + c2.re;
//        retc.im = c1.im + c2.im;
//
//        return retc;    //this one is very weird
//    }

    void display()
    {
        cout << endl << re << " + " << im << "i" << endl;
    }

    void mul(complex c1, complex c2)
    {
        cout << "in mul" << endl;
        re = c1.re*c2.re - c1.im*c2.im;
        im = c1.re*c2.im + c1.re*c2.im;
    }

    complex mul(complex c)
    {
        cout << "in mul" << endl;
        complex retc;
        retc.re = re*c.re - im*c.im;
        retc.im = re*c.im + c.re*im;

        return retc;
    }

    ~complex()
    {
        cout << re << " + " << im << "i" << endl;
        cout << "dest" << endl;
    }
};


int main()
{
    complex c1;

    c1.getdata();
    complex c2(5, 5);

    complex c3;
    c3.add(c1, c2);    //to store the answer of c1 + c2 we need c3 object

    c3.display();
    //perform c1 + c2 * c3
    complex c4;
    c4.add(c1, c2.mul(c3));    //can not use mul(c2, c3) for c2 * c3...why???!
    cout << "ans1" << endl;
    c4.display();

    //or we can also do...
    c1.add(c2.mul(c3));    //but this will modify c1
    cout << "ans2" << endl;
    c1.display();

    return 0;
}

following is the output

def const
enter real
1
enter im
2
parameterized
def const
in add
1 + 2i
dest
5 + 5i
dest

6 + 7i
def const
in mul
def const
in add
1 + 2i
dest
-5 + 65i
dest
6 + 7i
dest
ans1

-4 + 67i
in mul
def const
in add
-5 + 65i
dest
6 + 7i
dest
ans2

-4 + 67i
-4 + 67i
dest
6 + 7i
dest
5 + 5i
dest
-4 + 67i
dest

any idea why do destructors get called in the middle of nowhere!?

why some times destructor gets called in the middle of the code?

Take these methods for illustration:

void mul(complex c1, complex c2)
{
    cout << "in mul" << endl;
    re = c1.re*c2.re - c1.im*c2.im;
    im = c1.re*c2.im + c1.re*c2.im;
}

void add(complex c1, complex c2)
{
    cout << "in add" << endl;
    re = c1.re + c2.re;
    im = c1.im + c2.im;
}

You invoke it with:

c4.add(c1, c2.mul(c3));

Both add() and mul() are receiving their parameters by value. When the compiler sees a pass-by-value parameter, it creates a new version of the parameter object by means of the copy constructor. All classes have a default copy constructor, which assigns each field member one by one. This new version is used throughout the method, and finally destroyed.

So, when you call mul() in c2 with c3 as parameter, a new complex object is created from c3 , and when the end of mul() is reached, it is destroyed. The same happens with the call to add() with c1 and the result of c2.mul(c3) .

If you want to avoid this copying (which takes time and resources), then you should change the kind of parameter passing in your functions. Specifically, you can pass them by pointer or by reference. The problem is that this would allow modifications of them inside the function: but in the concrete case of pass-by-reference, you can modify it with const , which gets you the best of both worlds: you get the object efficiently passed without the possibility of modification.

void mul(const complex &c1, const complex &c2)
{
    cout << "in mul" << endl;
    re = c1.re*c2.re - c1.im*c2.im;
    im = c1.re*c2.im + c1.re*c2.im;
}

void add(const complex &c1, const complex &c2)
{
    cout << "in add" << endl;
    re = c1.re + c2.re;
    im = c1.im + c2.im;
}

Taking into account that the methods above do not modify the instance they are being called against, they can be also const themselves.

void mul(const complex &c1, const complex &c2) const
{
    cout << "in mul" << endl;
    re = c1.re*c2.re - c1.im*c2.im;
    im = c1.re*c2.im + c1.re*c2.im;
}

void add(const complex &c1, const complex &c2) const
{
    cout << "in add" << endl;
    re = c1.re + c2.re;
    im = c1.im + c2.im;
}

Also, since these functions do not require an instance of complex , they could also be independent, friend functions, or static methods. Actually, that is worth another answer.

Just as a rule of thumb, when you have just one argument of the same class, as in void mul(complex c2) , that's probably a member of the class; you're going to invoke it as c1.mul( c2 ) . When you have two arguments, as in void mul(complex c1, complex c2) then it is an independent function (ie, you'll invoke it as mul( c1, c2 ) , that it can be friend if you want it wrapped inside your class or access to the private member of your class. Normally you create these friend functions because you have an operator with an object of another class (or a primitive) at its left. Another issue is that an independent function should better return a new object instead of modifying one of its arguments... as you can see, it gets more and more complex.

Anyway, these are the signatures your methods should sport:

class complex {
public:
// ... more things...
    void mul(complex c2);
    complex operator*(const complex &c2);
    friend complex operator*(int x, const complex &c1);
// ... more things...
};

Also, instead of tying your class to the console with the display() function, better overload the operator <<, and you'll be able to use that functionality with any stream.

Find the complete code in IDEOne .

Hope this helps.

Destructors are called when you delete a pointer explicitly, at the end of the scope for scope variables and at the end of the statement for temporaries.

In your case in

c4.add(c1, c2.mul(c3));

you need to compute c2.mul(c3). It will create a new complex class instance. It will be kept around for add to execute, and it will be destroyed when the call is done, since it's no longer needed.

First of all keep in mind that you should only concern about calling a destructor for an object created dynamically in run time. One straight forward answer for this is, YOU need to verify your codes carefully and find out all the dynamically created objects in your codes and check exactly when it is going to out of scope ie the time/area from when that object is never going to use any more, and exactly that point of time you need to call the destructor of that object to clear heap memory. Please keep in mind that destructor is for releasing/cleaning memory to avoid memory leak and better memory management in a process.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM