Learning C++: The Constructor And Destructor


When programming with C++, programmers do not need to call destructors
explicitely. They are called by the compiler before the end of objects. Sometimes,
it is confusing that how the compilers deal with the destructors.
When it comes to the constructors, things need to be clear when the normal
constructors (means non-copy constructor in this article) called, and when the
copy constructors called.
In this article, I present what they are with demonstrating them by experiments.
The platform I use is:
OS: SuSE10.2
compiler: gcc 4.1.2 20061115 (prerelease)
 

First, I write down the fact as follow:
1. destructors will created by the compiler if none provided by programmers.
2. if the programmes define the destructor, the compiler won’t created a
destructor even if the programmer-defined one does nothing.
3. for a object which had other objects as its members, the compiler will
call its members’ destructors when the object ends its life. Even the
programmers define destructor doing nothing.
4. when the programmer does not define its own copy constructor, the compiler
will created one by default.
5. if default copy constructor is used, then the object’s members’ copy constructors
are called. So is its base class’s.
6. if the programmer define one copy constructor, then the object’s members’
copy constructors won’t be called automatically, they must call explicitely.
So is its base class’s.


For each point above, I’ll illustrate them with some examples.
first, there is two source codes about the destructor.

example code 1, with the programmer-defined destructor:
/**
 * file: default_destructor.cpp
 * author: kceiw (kceiw@163.com)
 * created time: Oct 30, 2007
 * description: use to demenstrate the members' destructors are called automatically
 *              in the user-defined destructor.
 */


#include <string>
using namespace std;


// declare a class with default destructor
class se {
public:
        void print() {}
        ~se(){ int i=0; i++;}
private:
        string s;
};


int main(int argc, char *argv[])
{
        se s;
        s.print();
        return 0;
}

example code 2, with the default destructor:
/**
 * file: default_destructor.cpp
 * author: kceiw (kceiw@163.com)
 * created time: Oct 30, 2007
 * description: demonstrates that a default constructor is created by the compiler, in which
 *              members' destructors are called.
 */

#include <string>
using namespace std;

// declare a class with default destructor
class de {
public:
        void print() {}

private:
        string s;
};


int main(int argc, char *argv[])
{
        de d;
        d.print();
        return 0;
}


When generating the assembly codes using gcc -S -o outputfile sourcefile, we
can have an insight what the compiler has done with them.
As for the first example, there are two code segments as below:

main:
.LFB874:
        leal    4(%esp), %ecx
.LCFI8:
        andl    $-16, %esp
        pushl   -4(%ecx)
.LCFI9:
        pushl   %ebp
.LCFI10:
        movl    %esp, %ebp
.LCFI11:
        pushl   %ebx
.LCFI12:
        pushl   %ecx
.LCFI13:
        subl    $32, %esp
.LCFI14:
        leal    -12(%ebp), %eax
        movl    %eax, (%esp)
        call    _ZN2seC1Ev
        leal    -12(%ebp), %eax
        movl    %eax, (%esp)
        call    _ZN2se5printEv
        movl    $0, %ebx
        leal    -12(%ebp), %eax
        movl    %eax, (%esp)
        call    _ZN2seD1Ev
        movl    %ebx, %eax
        addl    $32, %esp
        popl    %ecx
        popl    %ebx
        popl    %ebp
        leal    -4(%ecx), %esp
        ret

Pleas note that in the main function, it calls ZN2seD1Ev, which is the destructor
of the object s. And in ZN2seD1Ev, which is below:
_ZN2seD1Ev:
.LFB873:
        pushl   %ebp
.LCFI2:
        movl    %esp, %ebp
.LCFI3:
        subl    $24, %esp
.LCFI4:
        movl    $0, -4(%ebp)
        addl    $1, -4(%ebp)
        movl    8(%ebp), %eax
        movl    %eax, (%esp)
        call    _ZNSsD1Ev
        leave
        ret

it can be seen that the string’s destructor ZNSsD1Ev is called. so when we
generate the assembly code for example 2, we get the similar result. That is
to say, when one object end its life, all its members must be destructed by the
compiler.


Here is an example illustrate the constructor and copy constructor.

/**
 * file: copy.cpp
 * author: kceiw (kceiw@163.com)
 * created dated: Oct 30, 2007
 * description: demonstrates that when a class without user-defined copy
 *              constructor, call its members' copy constructors(user-defined
 *              or default) are called automatically.  and when a user-defined
 *              copy constructor exists, its members' copy constructors are not
 *              called, except explicit called in the copy constructor.
 */

#include <string>
#include <iostream>
using namespace std;

class B
{
public:
        B() {ii = 8; cout << "constructor in B, i=" << ii << endl;}
        B(int i) {ii=i; cout << "constructor with argument in B, i=" << ii << endl;}
        B(const B &b) {this->ii = b.ii; cout << "copy constructor in B, i=" << ii << endl;}
        ~B() {}
        int getI() {return ii;}
        void setI(int i) { ii = i; }
private:
        int ii;
};

class WithCC
{
public:
        WithCC():b(1){}
        WithCC(const WithCC &a) {}
        int getI() { return b.getI(); }

private:
        B b;
};

class WithoutCC
{
public:
        WithoutCC() : b(2) {}
        int getI() { return b.getI(); }

public:
        B b;
};


class Explicit
{
public:
        Explicit() : b(3) {}
        Explicit(const Explicit &e) : b(e.b){  }
        int getI() { return b.getI(); }

private:
        B b;
};


class Child1 : public B
{
public:
        Child1() : B(10) {}
        Child1(const Child1 &c) { }
};

class Child2 : public B
{
public:
        Child2() : B(10) {}
};

class Child3 : public B
{
public:
        Child3() : B(10) {}
        Child3(const Child3 &c) : B(c) {}
};

int main(int argc, char *argv[])
{
        WithCC a;
        WithCC aa(a);
        cout << "the composited object's copy constructor isn't called" << endl;
        cout << a.getI() << "/t" << aa.getI() << endl;

        WithoutCC c;
        WithoutCC cc(c);
        cout << "the composited object's copy constructor is called" << endl;
        cout << c.getI() << "/t" << cc.getI()<< endl;

        Explicit e1;
        Explicit e2(e1);
        cout << "call the member's copy constructor explicitely." << endl;
        cout << e1.getI() << "/t" << e2.getI() << endl;

        cout << "the father's copy constructor won't be called automatically if"
                << " user-defined "
                << "copy constructor is called." << endl;
        Child1 c1;
        Child1 c11(c1);
        cout << c1.getI() << "/t" << c11.getI() << endl;

        cout << "the father's copy constructor will be called if the child class"
                << " has no user-defined copy constructor" << endl;
        Child2 c2;
        Child2 c22(c2);
        cout << c2.getI() << "/t" << c22.getI() << endl;

        cout << "the father's copy constructor is called explicitely." << endl;
        Child3 c3;
        Child3 c33(c3);
        cout << c3.getI() << "/t" << c33.getI() << endl;

        return 0;
}

and when the code is compiled, linked, and run, we get the output as follow:
constructor with argument in B, i=1
constructor in B, i=8
the composited object’s copy constructor isn’t called
1 8
constructor with argument in B, i=2
copy constructor in B, i=2
the composited object’s copy constructor is called
2 2
constructor with argument in B, i=3
copy constructor in B, i=3
call the member’s copy constructor explicitely.
3 3
the father’s copy constructor won’t be called automatically if user-defined
copy constructor is called.
constructor with argument in B, i=10
constructor in B, i=8
10 8
the father’s copy constructor will be called if the child class has no
user-defined copy constructor
constructor with argument in B, i=10
copy constructor in B, i=10
10 10
the father’s copy constructor is called explicitely.
constructor with argument in B, i=10
copy constructor in B, i=10
10 10


From the result, we can conclud that: the composited object’s copy constructor
and its base class copy constructor must be called explicitely when the
programmer define copy constructor. Otherwise, the compiler will call their
normal constructor instead.


Reference:
C++ Primier
Thinking In C++

Acknowledgement:
黄佳洲
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值