C语言
主页 > 软件编程 > C语言 >

解读构造函数的调用规则、深拷贝与浅拷贝

2024-11-14 | 佚名 | 点击:

1.调用规则

默认情况下,C++至少会给一个类添加三个函数:

调用规则:

1.如果用户定义了有参构造函数,C++将不在提供默认无参构造函数,但是会提供默认拷贝构造函数。

如上图,我们给类A提供了一个有参的构造函数,所以此时,如果再去主函数定义一个无参的对象时,编译器就会提示“类A不存在默认构造函数”,这就说明如果自己定义了有参构造函数,那么就不会提供默认的无参构造函数。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

#include<iostream>

using namespace std;

class A {

public:

    int num;

    A(int num) {

        this->num = num;

    }

};

int main()

{

    A a(5);

    A b(a);

    cout << b.num;

    return 0;

}

在类中我们只提供了一个有参的构造函数,我们在主函数中定义一个b对象,传的是对象a,那么它便会走拷贝构造的函数,如果能够输出b.num的值为5的话,则说明提供了默认的拷贝构造函数。

通过打印b的num,发现确实是5.那么就会提供默认的构造函数。

2.如果用户定义拷贝构造函数,c++不会提供其他构造函数

可以看到当我们仅在类A中写了一个拷贝构造函数时,再去主函数中定义一个无参的对象a时,就会报错,提示“类A不存在默认构造函数”。

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

#include<iostream>

using namespace std;

class A {

    int num;

    int* p;

public:

    A() {

        num = 0;

        p = nullptr;

        cout << "调用无参构造" << endl;

    }

    A(int a) {

        num = a;

        p = new int[num];

        cout << "调用有参构造" << endl;

    }

    ~A() {

        if (p)delete[]p;

    }

};

 

int main() {

    A a(3);

    A b = a;

    return 0;

}

在类A中我们定义了一个指针变量,在有参构造函数中,我们在堆区开辟了一块空间。在主函数数中,定义对象b要走拷贝构造函数。下面我们来运行这段代码:

发现报错了,原因就是对象a和对象b的成员变量*p指向的是同一块堆区内存,再调用析构函数时,对象b先把这块堆区内存释放了,当对象a调用析构函数时,此时那块堆区内存已经不存在了,所以会出现访问内存失败,导致程序崩溃。

那么解决的办法就是重写拷贝构造函数,让他们指向不同的堆区区域。代码如下:

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

#include<iostream>

using namespace std;

class A {

    int num;

    int* p;

public:

    A() {

        num = 0;

        p = nullptr;

        cout << "调用无参构造" << endl;

    }

    A(int a) {

        num = a;

        p = new int[num];

        cout << "调用有参构造" << endl;

    }

    A(const A& other) {//万能引用,避免实参修改形参

        num = other.num;

        p = new int[num];//保证内存大小相同

        for (int i = 0; i < num; i++) {

            p[i] = other.p[i];//保证数据相同

        }

        cout << "调用拷贝构造" << endl;

    }

    ~A() {

        if (p)delete[]p;

        cout << "调用析构函数" << endl;

    }

};

 

int main() {

    A a(3);

    A b = a;

    return 0;

}

通过再在堆区上开辟一块内存,让这块堆区内存上的内容和对象a的堆区内容一样就行,这就是深拷贝。

程序能正常运行。

3.string类的拷贝构造练习

可以通过下面这个自定义string类进一步加深对深拷贝的理解。

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

#include<iostream>

using namespace std;

class String {

    int size;

    char* p;

public:

    String() {

        p = nullptr;

        size = 0;

    }

    String(const char* p) {

        size = strlen(p);

        this->p = new char[size + 1];//\0,所以加一

        strcpy_s(this->p, size + 1, p);

    }

    ~String() {

        if (p)  delete[]p;

    }

    String(const String& other) {

        this->size = other.size;

        this->p = new char[size + 1];

        strcpy_s(this->p, size + 1,other.p);

    }

    void print() {

        cout << p<<endl;

    }

};

int main() {

    String str = "abcde";

    String str2(str);

    str2.print();

    return 0;

}

原文链接:
相关文章
最新更新