C++构造函数

参考:C++-copy constructor、copy-assignment operator、destructor

     Copy constructors, assignment operators, and exception safe assignment

C++在对象的不同创建方法中,会调用不同的构造函数,下面的代码探讨了调用一般的默认构造函数和复制构造函数的情形

class A
{
private:
    int v;
public:
    A()
    {
        v = 0;
        cout << "object created" << endl;
    }
    A(const A& a) { cout << "copy construct - const" << endl;}
    A(A& a) { cout << "copy construct" << endl;}
    explicit A(int _v) {
        v = _v;
        cout << "construct - int param" << endl;
    }

    ~A() { cout << "object deleted" << endl; }

    void setvalue(int value = 6);
    int getvalue() { return v; }
};

void A::setvalue(int value)
{
    v = value;
}

void ReferenceA(A& a)
{
    cout << "Reference a" << endl;
}

void ReferenceA(const A& a)
{
    cout << "Reference const a" << endl;
}

void ParameterA(A a)
{
    cout << "Parameter a" << endl;
}

int main() {
    A a1;                   //calls A(), before function return, calls ~A()
    A *b = new A();         //calls only A()
    delete b;               //calls only ~A(), it must be with new

    A a2 = a1;              //calls A(A& a), before function return, calls ~A()
    A a3(a1);               //calls A(const A& a), before function return, calls ~A()
    A(a4);                  //calls A(), before function return, calls ~A()
    A a6 = A(a4);           //calls A(const A& a), before function return, calls ~A()
    A *a8 = new A(a1);      //calls only A(const A& a)
    delete a8;              //calls only ~A(), it must be with new
    A a9(3);
    cout << "v=" << a9.getvalue() << endl;
    const A a10;            //calls A(), before function return, calls ~A()
    A a11(a10);             //calls A(const A& a), before function return, calls ~A()
    A a12 = a10;            //calls A(const A& a), before function return, calls ~A()

    ReferenceA(a1);         //don't call construction, invoke ReferenceA(A& a)
    ReferenceA(a10);        //don't call construction, invoke ReferenceA(const A& a)
    ParameterA(a1);         //calls A(A& a), before function ParameterA return, calls ~A()
    ParameterA(a10);        //calls A(A& a), before function ParameterA return, calls ~A()

    a1.setvalue();
    cout << "v=" << a1.getvalue() << endl;
    //system("pause");

    return 0;
}

在上述代码中,要注意函数的默认参数只能出现在函数的定义或声明中,不能同时出现在定义和声明中。

使用CentOS gcc version 8.2.0 (GCC)编译器所得结果如下:

object created
object created
object deleted
copy construct
copy construct
object created
copy construct
copy construct
object deleted
construct - int param
v=3
object created
copy construct - const
copy construct - const
Reference a
Reference const a
copy construct
Parameter a
object deleted
copy construct - const
Parameter a
object deleted
v=6
object deleted
object deleted
object deleted
object deleted
object deleted
object deleted
object deleted
object deleted
object deleted

下面是一个C++构造函数调用的具体的例子,由于类中没有给出复制构造函数的定义导致了意想不到的问题。

strngbad.h

 1 // strngbad.h -- flawed string class definition
 2 #include <iostream>
 3 #ifndef STRNGBAD_H_
 4 #define STRNGBAD_H_
 5 class StringBad
 6 {
 7 private:
 8     char *str;                 // pointer to string
 9     int len;                   // length of string
10     static int num_strings;    // number of objects
11 
12 public:
13     StringBad(const char * s); // constructor
14     StringBad();               // default constructor
15     ~StringBad();              // destructor
16 // friend function
17     friend std::ostream& operator<<(std::ostream & os,
18                          const StringBad & st);
19 };
20 #endif
View Code

strngbad.cpp

 1 // strngbad.cpp -- StringBad class methods
 2 #include <cstring>                          // string.h for some
 3 #include "strngbad.h"
 4 using std::cout;
 5 
 6 // initializing static class member
 7 int StringBad::num_strings = 0;
 8 
 9 // class methods
10 
11 // construct StringBad from C string
12 StringBad::StringBad(const char * s)
13 {
14     len = std::strlen(s);                   // set size
15     str = new char[len + 1];                // allot storage
16     std::strcpy(str, s);                    // initialize pointer
17     num_strings++;                          // set object count
18     cout << num_strings << ": \"" << str
19          << "\" object created\n";          // For Your Information
20 }
21 
22 StringBad::StringBad()                      // default constructor
23 {
24     len = 4;
25     str = new char[4];
26     std::strcpy(str, "C++");                // default string
27     num_strings++;
28     cout << num_strings << ": \"" << str
29          << "\" default object created\n";  // FYI
30 }
31 
32 StringBad::~StringBad()                     // necessary destructor
33 {
34     cout << "\"" << str << "\" object deleted, ";   // FYI
35     --num_strings;                          // required
36     cout << num_strings << " left\n";       // FYI
37     delete [] str;                          // required
38 }
39 
40 std::ostream& operator<<(std::ostream & os, const StringBad & st)
41 {
42     os << st.str;
43     return os; 
44 }
View Code

vegnews.cpp

 1 // vegnews.cpp -- using new and delete with classes
 2 // compile with strngbad.cpp
 3 #include <iostream>
 4 using namespace std;
 5 
 6 #include "strngbad.h"
 7 
 8 void callme1(StringBad &);  // pass by reference
 9 void callme2(StringBad);    // pass by value
10 
11 int main()
12 {
13     using std::endl;
14     StringBad headline1("Celery Stalks at Midnight");
15     StringBad headline2("Lettuce Prey");
16     StringBad sports("Spinach Leaves Bowl for Dollars");
17     cout << "headline1: " << headline1 << endl;
18     cout << "headline2: " << headline2 << endl;
19     cout << "sports: " << sports << endl;
20     callme1(headline1);
21     cout << "headline1: " << headline1 << endl;
22     callme2(headline2);
23     cout << "headline2: " << headline2 << endl;
24     cout << "Initialize one object to another:\n";
25     StringBad sailor = sports;
26     cout << "sailor: " << sailor << endl;
27     cout << "Assign one object to another:\n";
28     StringBad knot;
29     knot = headline1;
30     cout << "knot: " << knot << endl;
31     cout << "End of main()\n";
32     
33     return 0;
34 }
35 
36 void callme1(StringBad & rsb)
37 {
38     cout << "String passed by reference:\n";
39     cout << "    \"" << rsb << "\"\n";
40 }
41 
42 void callme2(StringBad sb)
43 {
44     cout << "String passed by value:\n";
45     cout << "    \"" << sb << "\"\n";
46 }
View Code

使用msvc2013编译器所得结果如下

1: "Celery Stalks at Midnight" object created
2: "Lettuce Prey" object created
3: "Spinach Leaves Bowl for Dollars" object created
headline1: Celery Stalks at Midnight
headline2: Lettuce Prey
sports: Spinach Leaves Bowl for Dollars
String passed by reference:
    "Celery Stalks at Midnight"
headline1: Celery Stalks at Midnight
String passed by value:
    "Lettuce Prey"
"Lettuce Prey" object deleted, 2 left
headline2: 葺葺葺葺葺葺葺葺
Initialize one object to another:
sailor: Spinach Leaves Bowl for Dollars
Assign one object to another:
3: "C++" default object created
knot: Celery Stalks at Midnight
End of main()
"Celery Stalks at Midnight" object deleted, 2 left
"Spinach Leaves Bowl for Dollars" object deleted, 1 left
"葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺." object deleted, 0 left

使用gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4)编译器所得结果如下

1: "Celery Stalks at Midnight" object created
2: "Lettuce Prey" object created
3: "Spinach Leaves Bowl for Dollars" object created
headline1: Celery Stalks at Midnight
headline2: Lettuce Prey
sports: Spinach Leaves Bowl for Dollars
String passed by reference:
    "Celery Stalks at Midnight"
headline1: Celery Stalks at Midnight
String passed by value:
    "Lettuce Prey"
"Lettuce Prey" object deleted, 2 left
headline2: 
Initialize one object to another:
sailor: Spinach Leaves Bowl for Dollars
Assign one object to another:
3: "C++" default object created
knot: Celery Stalks at Midnight
End of main()
"Celery Stalks at Midnight" object deleted, 2 left
"Spinach Leaves Bowl for Dollars" object deleted, 1 left
"�v" object deleted, 0 left
*** Error in `/media/xzgz/Code/practice/Code/C++/c++_project/CPlusGrammer/build-cpg-Desktop-Debug/cpg': double free or corruption (fasttop): 0x000000000076a080 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7f62bf89b7e5]
/lib/x86_64-linux-gnu/libc.so.6(+0x8037a)[0x7f62bf8a437a]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7f62bf8a853c]
/media/xzgz/Code/practice/Code/C++/c++_project/CPlusGrammer/build-cpg-Desktop-Debug/cpg[0x400cd5]
/media/xzgz/Code/practice/Code/C++/c++_project/CPlusGrammer/build-cpg-Desktop-Debug/cpg[0x40102c]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f62bf844830]
/media/xzgz/Code/practice/Code/C++/c++_project/CPlusGrammer/build-cpg-Desktop-Debug/cpg[0x400a09]
======= Memory map: ========
00400000-00402000 r-xp 00000000 08:05 619273                             /media/xzgz/Code/practice/Code/C++/c++_project/CPlusGrammer/build-cpg-Desktop-Debug/cpg
00601000-00602000 r--p 00001000 08:05 619273                             /media/xzgz/Code/practice/Code/C++/c++_project/CPlusGrammer/build-cpg-Desktop-Debug/cpg
00602000-00603000 rw-p 00002000 08:05 619273                             /media/xzgz/Code/practice/Code/C++/c++_project/CPlusGrammer/build-cpg-Desktop-Debug/cpg
00758000-0078a000 rw-p 00000000 00:00 0                                  [heap]
7f62b8000000-7f62b8021000 rw-p 00000000 00:00 0 
7f62b8021000-7f62bc000000 ---p 00000000 00:00 0 
7f62bf51b000-7f62bf623000 r-xp 00000000 08:09 2753340                    /lib/x86_64-linux-gnu/libm-2.23.so
7f62bf623000-7f62bf822000 ---p 00108000 08:09 2753340                    /lib/x86_64-linux-gnu/libm-2.23.so
7f62bf822000-7f62bf823000 r--p 00107000 08:09 2753340                    /lib/x86_64-linux-gnu/libm-2.23.so
7f62bf823000-7f62bf824000 rw-p 00108000 08:09 2753340                    /lib/x86_64-linux-gnu/libm-2.23.so
7f62bf824000-7f62bf9e4000 r-xp 00000000 08:09 2753345                    /lib/x86_64-linux-gnu/libc-2.23.so
7f62bf9e4000-7f62bfbe4000 ---p 001c0000 08:09 2753345                    /lib/x86_64-linux-gnu/libc-2.23.so
7f62bfbe4000-7f62bfbe8000 r--p 001c0000 08:09 2753345                    /lib/x86_64-linux-gnu/libc-2.23.so
7f62bfbe8000-7f62bfbea000 rw-p 001c4000 08:09 2753345                    /lib/x86_64-linux-gnu/libc-2.23.so
7f62bfbea000-7f62bfbee000 rw-p 00000000 00:00 0 
7f62bfbee000-7f62bfc04000 r-xp 00000000 08:06 669579                     /media/xzgz/Ubuntu/Ubuntu/Caffe/anaconda2/lib/libgcc_s.so.1
7f62bfc04000-7f62bfe03000 ---p 00016000 08:06 669579                     /media/xzgz/Ubuntu/Ubuntu/Caffe/anaconda2/lib/libgcc_s.so.1
7f62bfe03000-7f62bfe04000 rw-p 00015000 08:06 669579                     /media/xzgz/Ubuntu/Ubuntu/Caffe/anaconda2/lib/libgcc_s.so.1
7f62bfe04000-7f62bff76000 r-xp 00000000 08:06 711828                     /media/xzgz/Ubuntu/Ubuntu/Caffe/anaconda2/lib/libstdc++.so.6
7f62bff76000-7f62c0176000 ---p 00172000 08:06 711828                     /media/xzgz/Ubuntu/Ubuntu/Caffe/anaconda2/lib/libstdc++.so.6
7f62c0176000-7f62c0180000 r--p 00172000 08:06 711828                     /media/xzgz/Ubuntu/Ubuntu/Caffe/anaconda2/lib/libstdc++.so.6
7f62c0180000-7f62c0182000 rw-p 0017c000 08:06 711828                     /media/xzgz/Ubuntu/Ubuntu/Caffe/anaconda2/lib/libstdc++.so.6
7f62c0182000-7f62c0186000 rw-p 00000000 00:00 0 
7f62c0186000-7f62c01ac000 r-xp 00000000 08:09 2753323                    /lib/x86_64-linux-gnu/ld-2.23.so
7f62c0384000-7f62c0389000 rw-p 00000000 00:00 0 
7f62c03a8000-7f62c03ab000 rw-p 00000000 00:00 0 
7f62c03ab000-7f62c03ac000 r--p 00025000 08:09 2753323                    /lib/x86_64-linux-gnu/ld-2.23.so
7f62c03ac000-7f62c03ad000 rw-p 00026000 08:09 2753323                    /lib/x86_64-linux-gnu/ld-2.23.so
7f62c03ad000-7f62c03ae000 rw-p 00000000 00:00 0 
7ffece974000-7ffece995000 rw-p 00000000 00:00 0                          [stack]
7ffece9a0000-7ffece9a2000 r--p 00000000 00:00 0                          [vvar]
7ffece9a2000-7ffece9a4000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

需要注意的是,在StringBad类中,static int num_strings是静态成员变量,它由该类实例化的所有对象所共享,不能在类声明中初始化静态成员变量,可以在类声明之外使用单独的语句来进行初始化。

posted @ 2017-09-04 23:26  洗盏更酌  Views(350)  Comments(0Edit  收藏  举报