构造函数、析构函数、拷贝构造函数、赋值构造函数、取地址运算符重载

一、构造函数的定义和使用

  1、函数名和类名相同

  2、构造函数无函数返回类型说明。即什么也不写,实际上构造函数有返回值,返回的就是构造函数所创建的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Test
{
public:
    Test()
    {
        cout << "Creat Test Object:" << this << endl;
        data = 0;
    }
private:
    int data;
};
void main()
{
    Test t1;
    printf("%p",&t1);
}

  

  3、在程序运行时,当新的对象被建立,该对象所属的类的构造函数自动被调用,在该对象的生存周期中也只被调用一次。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "Test.h"
class Test
{
public:
    Test()
    {
        cout << "Creat Test Object:" << this << endl;
        data = 0;
    }
private:
    int data;
};
void main()
{
    Test t1;
    Test t2;
    Test t3;
}
//output
//Creat Test Object : 004FF914
//Creat Test Object : 004FF908
//Creat Test Object : 004FF8FC

  

  4、构造函数可以重载。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Test
{
public:
    Test()
    {
        cout << "Creat Test Object:" << this << endl;
        data = 0;
    }
    Test(int d)
    {
        data = d;
    }
 
    Test(int d,int x, int y)
    {
        data = d;
        this->x = x;
        this->y = y;
    }
private:
    int data;
    int x;
    int y;
    int d;
};
void main()
{
    Test t1;//这里的t1后面没有()
    Test t2(10);
    Test t3(10,2,4);
}

  

  5、构造函数可以在类中定义,也可以在类外定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include "Test.h"
class Test
{
public:
    Test()
    {
        cout << "Creat Test Object:" << this << endl;
        data = 0;
    }
    Test(int x, int y);
 
    Test(int d)
    {
        data = d;
    }
 
    Test(int d, int x, int y)
    {
        data = d;
        this->x = x;
        this->y = y;
    }
private:
    int data;
    int x;
    int y;
    int d;
};
Test::Test(int x, int y)
{
    this->x = x;
    this->y = y;
}
void main()
{
    Test t1;
    Test t2(10);
    Test t3(10,2,4);
    Test t4(20, 30);
}

  

  6、如果类中没有给构造函数,则C++编译器自动给出一个缺省的构造的函数:

      只要构造函数是无参的或者只要各参数均有缺省值的,C++编译器都都认为是缺省的构造函数,并且缺省的构造函数只有一个。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include "Test.h"
class Test
{
public:
     
    Test(int x=1, int y=2);
 
private:
    int data;
    int x;
    int y;
    int d;
};
Test::Test(int x, int y)
{
    this->x = x;
    this->y = y;
    cout << "Creat Test Object:" << this << endl;
}
void main()
{
    Test t1;
}

  7、如果对象的数据成员全为公有的,也可以在对象名后加 “=” 加 “{ }”。在花括号中顺序填入全体数据成员的初始值。

 

二、析构函数定义和使用

当一个对象定义时,C++自动调用构造函数建立该对象并进行初始化,那么当一个对象的生命周期结束时,C++也会自动调用一个函数注销该对象并进行善后工作,这个特殊的成员函数就是析构函数。

  1、函数名和类名相同,但在前面要加‘~’

  2、无函数返回类型,不带任何参数

  3、一个类有一个也只有一个析构函数(相当于java中的static函数,所有对象共用这个函数,只需要把各自对象地址传递过去即可,实现原理就是用的this指针)

  4、对象注销时,系统自动调用析构函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include "Test.h"
class Test
{
public:
     
    Test()
    {
        cout << "Creat Test Obj:" << this << endl;
    }
    ~Test()
    {
        cout << "Free Test Obj:" << this << endl;
    }
 
public:
    int data;
    int x;
    int y;
    int d;
};
 
void main()
{
    Test t1 ;
    Test t2;
    Test t3;
    Test t4;
}

  

 

 上图构造顺序是1 2 3 4  析构顺序是4 3 2 1

因为构造是入栈的过程,析构是出栈的过程

三、自动装箱和自动拆箱

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include "Test.h"
class Test
{
public:
     
    Test(int d=0)
    {
        cout << "Creat Test Obj:" << this << endl;
        data = d;
        cout << d << endl;
    }
    ~Test()
    {
        cout << "Free Test Obj:" << this << endl;
    }
    operator int()
    {
        return data;
    }
public:
    int data;
    int x;
    int y;
};
 
void main()
{
    Test t1 = 200;
    t1 = 100;//自动装箱,调用Test(int d=0)构造函数 给data赋值     cout <<"t1.data="<<t1.data<<endl;      int value;      value = t1; //自动拆箱,调用operator int() 返回data值     cout << "value=" << value << endl; }

 这是我表面理解成自动装箱和自动拆箱,实际上这跟java的自动拆装箱原理还是不太一样的

1
t1 = 100;  t1是Test类型 100是整型,要想进行赋值操作,就得找个中间的Test类型的变量用来接收100<br>同理double a = 22.14;int b = a;实际上是找了中间变量c保存22,再赋值给b<br><br>三、引用<br>  1、对变量引用
1
2
int a = 10;
int &b = a;

  2、对指针引用

1
2
3
int a = 10;
int *p = &a;
int *&q = p;//指针q引用指针p

  3、对数组引用

1
2
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int(&brr)[10] = arr;

  4、常引用

1
2
3
4
5
6
const int x = 100;
const int &y = x;
 
int n = 20;
const int &m = n;
    

  5、诡异的引用

1
2
const double d = 12.34;
const int &f = d;

  debug查看d和f的地址,以及值如下:

 

 &d  != &f 说明类型不一样时,d截取12保存在临时空间,再把临时空间起个别名叫f

 

四、拷贝构造函数

 同一个类的对象在内存中有完全相同的解构,如果进行整体复制是完全可行的,这个拷贝过程只需要拷贝数据成员,而函数成员是公用的。所以在创建对象时,可以用同一个类的另一个对象来初始化该对象,这时所用到的构造函数称为拷贝构造函数,当不编写拷贝构造函数时,默认编译器会有一个拷贝构造函数

拷贝构造函数使用的三个场景:

  1、直接赋值

 

1
2
Test t2(t1);//拷贝构造
Test t3 = t1;//拷贝构造

  

  2、当函数参数是对象时,会调用拷贝构造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include "iostream"
using namespace std;
class Test
{
public:
    Test(int d = 0)
    {
        cout << "Creat default construct" << endl;
        data = d;
    }
    Test(const Test& t)//拷贝构造函数
    {
         
        cout << "Creat copy construct" << this << endl;
        data = t.data;
    }
    Test& operator = (const Test& t)//赋值构造函数
    {
        if (this != &t)
        {
            data = t.data;
        }
        return *this;
    }
    ~Test()
    {
        cout << this << ":free this obj" << endl;
    }
public:
    int getData()
    {
        return data;
    }
private:
    int data;
};
int fun(Test x)
{
    int value;
    value = x.getData();
    return value;
void main()
{
    Test t1;
    fun(t1);
     
}

  3、函数返回值是对象时,调用拷贝构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include "iostream"
using namespace std;
class Test
{
public:
    Test(int d = 0)
    {
        cout << "Creat default construct" << endl;
        data = d;
    }
    Test(const Test& t)//拷贝构造函数,采用引用代码出现无穷拷贝构造函数的调用,其实不采用引用,编译会报错
    {
         
        cout << "Creat copy construct" << this << endl;
        data = t.data;
    }
    Test& operator = (const Test& t)//赋值构造函数,采用引用可以避免调用构造函数
    {
        if (this != &t)
        {
            data = t.data;
        }
        return *this;
    }
    ~Test()
    {
        cout << this << ":free this obj" << endl;
    }
public:
    int getData()
    {
        return data;
    }
private:
    int data;
};
Test fun(Test x)
{
    int value;
    value = x.getData();
    Test tmp(10);
    return tmp;
}
void main()
{
    Test t1;
    Test t2(t1);//拷贝构造
    Test t3 = t1;//拷贝构造
    Test t4;
    t4 = t1;//赋值构造
    Test t6 = fun(t1);//函数返回时已经拷贝构造了一个无名的对象,所以t6就不需要再调用拷贝构造函数了
    Test t5;
    t5 = fun(t1);//这里需要调用赋值构造函数
}

  

五、赋值构造函数

  C++中不编写赋值构造函数时,编译器会默认提供,功能就是把成员变量进行赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include "Test.h"
class Test
{
public:
    Test(int d=0)
    {
        cout << "Creat Test Obj:" << this << endl;
        data = d;
    }
    ~Test()
    {
        cout << "Free Test Obj:" << this << endl;
    }
public:
    int data;
};
 
void main()
{
    Test t1 = 10;
    Test t2;
    t2 = t1;
}

  上述代码就是把t1的成员变量值赋值给t2的成员变量,即t2里的data也是10。

自己定义赋值构造函数

  1、模板1 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include "Test.h"
class Test
{
public:
    Test(int d=0)
    {
        cout << "Creat Test Obj:" << this << endl;
        data = d;
    }
    Test(const Test &t)
    {
        cout << "Creat copy construct" << this << endl;
        data = t.data;
    }
    void operator= (Test t)
    {
        data = t.data;
    }
    ~Test()
    {
        cout << "Free Test Obj:" << this << endl;
    }
public:
    int data;
};
 
void main()
{
    Test t1 = 200;
    Test t2;
    t2 = t1;
}

 (1)这里在执行t2 = t1的时候会先执行一个拷贝构造函数(这样效率很低),再执行赋值构造函数,因为赋值构造函数里的形参是Test类对象

 (2)t2 = t1 其实就是t2.operate=(t1),就是t2调用operate()函数,

  2、模板2 对模板1的缺点:调用拷贝构造函数,效率低   优化,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include "Test.h"
class Test
{
public:
    Test(int d=0)
    {
        cout << "Creat Test Obj:" << this << endl;
        data = d;
    }
    Test(const Test &t)
    {
        cout << "Creat copy construct" << this << endl;
        data = t.data;
    }
    void operator= (const Test &t)//追加1、const 防止被修改 2、&应用,可以避免调用构造函数
    {
        ifthis != &t)//追加这个判断的作用是防止,对象自己给自己复制,避免效率低          {            data = t.data;          }
    }
    ~Test()
    {
        cout << "Free Test Obj:" << this << endl;
    }
public:
    int data;
};
 
void main()
{
    Test t1 = 200;
    Test t2;
    t2 = t1;
}

 这里还有个缺点,就是返回值问题,这里是void

  3、模板3 当出现连等赋值时,模板二就会编译出错

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include "Test.h"
class Test
{
public:
    Test(int d=0)
    {
        cout << "Creat Test Obj:" << this << endl;
        data = d;
    }
    Test(const Test &t)
    {
        cout << "Creat copy construct" << this << endl;
        data = t.data;
    }
    Test& operator= (Test &t) //修改两点: 1、返回值追加Test类型,这样main函数就可以使用连等运算 2、返回以引用返回,避免调用构造函数
    {
        if(this != &t)
        {
            data = t.data;
        }
        return *this;
         
    }
    ~Test()
    {
        cout << "Free Test Obj:" << this << endl;
    }
public:
    int data;
};
 
void main()
{
    Test t1 = 200;
    Test t2;
    Test t3;
    t3 = t2 = t1;//相当于t3.operator=(t2.operator=(t1)) 如果赋值构造函数无返回值,那么这个调用就会编译器报错
}

  

总结:模板3才是真正的模板,但也要注意,引用不能滥用,比如看下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include "Test.h"
class Test
{
public:
    Test(int d=0)
    {
        cout << "Creat Test Obj:" << this << endl;
        data = d;
    }
    Test(const Test &t)
    {
        cout << "Creat copy construct" << this << endl;
        data = t.data;
    }
    Test& operator= (Test &t)
    {
        if(this != &t)
        {
            data = t.data;
        }
        return *this;
         
    }
    ~Test()
    {
        cout << "Free Test Obj:" << this << endl;
    }
public:
    int GetData()
    {
        return data;
    }
private:
    int data;
};
 
Test& fun(Test x)
{
    int value;
    value = x.GetData();
    Test tmp(value);
    return tmp;
}
 
void main()
{
    Test t1 = 10;
    Test t2;
    t2 = fun(t1);
}

  debug时会发现,程序执行后t2里的data是个随机值,并不是10,说明t1给t2赋值失败,原因是什么呢?

  其实是因为调用fun函数时,返回的是引用且tmp是局部变量,出了fun函数就消失了,所以t2的data值并没有赋值成功。

  这就是引用引来的弊端

使用引用的条件:若函数返回的变量是局部变量,则别用引用返回,否则可以

  

 

  

posted @   念经似的zzz  阅读(160)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· 因为Apifox不支持离线,我果断选择了Apipost!
点击右上角即可分享
微信分享提示