C++拷贝构造函数和运算符重载--2

简介: C++拷贝构造函数和运算符重载--2

C++拷贝构造函数和运算符重载--1 https://developer.aliyun.com/article/1424584


请注意上面说的,默认拷贝构造只会进行浅拷贝,当我们在对象成员中开辟了动态空间时,使用默认拷贝构造将会出现问题。请看以下代码:

//此代码运行时将会崩溃
#include <iostream>
using namespace std;
typedef int DataType;
class Stack
{
public:
  Stack(size_t capacity = 10)
  {
  _array = (DataType*)malloc(capacity * sizeof(DataType));
  if (!_array) {
    perror("malloc申请空间失败");
    return;
  }
  _size = 0;
  _capacity = capacity;
  }
  ~Stack()
  {
  if (_array) {
    free(_array);
    _array = nullptr;
    _capacity = 0;
    _size = 0;
  }
  }
private:
  DataType* _array;
  size_t _size;
  size_t _capacity;
};
int main()
{
  Stack s1;
  Stack s2(s1);
  return 0;
}

分析:


       首先,s1先调用构造函数创建,在构造函数中开辟了10个元素的空间,然后,s2对象使用s1拷贝构造,Stack对象中没有自己定义,系统将生成一份默认的拷贝构造函数进行浅拷贝,这时,s1和s2栈结构将同时指向一块内存空间,当函数退出时,s2和s1会自动调用析构函数进行销毁,这就会造成s1和s2指向的同一块空间销毁两次,系统崩毁。


b1e1ae2c2a41475f981beff95dc0574c.png


       解决上面的问题不难,我们只需将s1和s2分别指向不同的空间即可,这时,需要我们自己定义拷贝构造函数。


#include <iostream>
using namespace std;
typedef int DataType;
class Stack
{
public:
  Stack(size_t capacity = 10)
  {
  _array = (DataType*)malloc(capacity * sizeof(DataType));
  if (!_array) {
    perror("malloc申请空间失败");
    return;
  }
  _size = 0;
  _capacity = capacity;
  }
  //定义拷贝构造
  Stack(const Stack& S)
  {
  _size = S._size;
  _capacity = S._capacity;
  _array = (DataType*)malloc(_capacity * sizeof(DataType));
  if (!_array) {
    perror("malloc申请空间失败");
    return;
  }
  }
  ~Stack()
  {
  if (_array) {
    free(_array);
    _array = nullptr;
    _capacity = 0;
    _size = 0;
  }
  }
private:
  DataType* _array;
  size_t _size;
  size_t _capacity;
};
int main()
{
  Stack s1;
  Stack s2(s1);
  return 0;
}

总:如果类中没有涉及空间资源的申请时,拷贝构造函数可以不写,但是一旦涉及到资源空间的申请时,则拷贝构造函数是一定要写上的。


拷贝构造函数典型调用场景:


       1,使用已存在对象创建新对象。如同以上Stack类中s2的创建。


/*..........*/
int main()
{
    Stack s1;
    Stack s2(s1);//使用已存在的对象s1来创建对象s2
    return 0;
}


       2,函数参数类型为类类型对象——解析:因为在传参过程中,形参就相当于实参的临时拷贝,相当于用实参来创建形参。


       3,函数返回值类型为类类型对象——解析:当函数返回时,函数内局部对象的生命周期结束,但是临时变量的值会被拷贝到调用函数的栈帧中,或者通过引用传递给调用函数。当返回类类型对象时,直接将此类对象拷贝到调用函数栈帧中。


总结一句话,只要是运用了类对象与类对象直接赋值进行初始化的情况,系统就会调用拷贝构造。而为了提高效率,一般能用引用就用引用。


C++拷贝构造函数和运算符重载--3 https://developer.aliyun.com/article/1424591?spm=a2c6h.13148508.setting.30.214f4f0emw3QR7

相关文章
|
1月前
|
C++
C++(十五) 运算符重载
C++中的运算符重载允许对已有运算符的功能进行重新定义,从而扩展语言功能、简化代码并提升效率。重载遵循特定语法,如 `friend 类名 operator 运算符(参数)`。重载时需注意不可新增或改变运算符数量、语义、优先级、结合性和返回类型。常见示例包括双目运算符 `+=` 和单目运算符 `-` 及 `++`。输入输出流运算符 `&lt;&lt;` 和 `&gt;&gt;` 也可重载。部分运算符只能作为成员函数重载。
|
4月前
|
C++ 容器
【C++】拷贝构造函数、拷贝赋值函数与析构函数
【C++】拷贝构造函数、拷贝赋值函数与析构函数
106 6
|
4月前
|
存储 编译器 C++
【C++】:拷贝构造函数和赋值运算符重载
【C++】:拷贝构造函数和赋值运算符重载
26 1
|
4月前
|
C++ 索引
C++核心技术要点《运算符重载》
C++核心技术要点《运算符重载》
52 2
|
3月前
|
自然语言处理 程序员 C++
C++基础知识(五:运算符重载)
运算符重载是C++中的一项强大特性,它允许程序员为自定义类型(如类或结构体)重新定义标准运算符的行为,使得这些运算符能够适用于自定义类型的操作。这样做可以增强代码的可读性和表达力,使得代码更接近自然语言,同时保持了面向对象编程的封装性。
|
3月前
|
Java 程序员 C++
|
3月前
|
编译器 C++
【C++】详解运算符重载,赋值运算符重载,++运算符重载
【C++】详解运算符重载,赋值运算符重载,++运算符重载
|
4月前
|
编译器 C++
【C++】类和对象③(类的默认成员函数:赋值运算符重载)
在C++中,运算符重载允许为用户定义的类型扩展运算符功能,但不能创建新运算符如`operator@`。重载的运算符必须至少有一个类类型参数,且不能改变内置类型运算符的含义。`.*::sizeof?`不可重载。赋值运算符`=`通常作为成员函数重载,确保封装性,如`Date`类的`operator==`。赋值运算符应返回引用并检查自我赋值。当未显式重载时,编译器提供默认实现,但这可能不足以处理资源管理。拷贝构造和赋值运算符在对象复制中有不同用途,需根据类需求定制实现。正确实现它们对避免数据错误和内存问题至关重要。接下来将探讨更多操作符重载和默认成员函数。
|
4月前
|
存储 编译器 C++
【C++】类和对象③(类的默认成员函数:拷贝构造函数)
本文探讨了C++中拷贝构造函数和赋值运算符重载的重要性。拷贝构造函数用于创建与已有对象相同的新对象,尤其在类涉及资源管理时需谨慎处理,以防止浅拷贝导致的问题。默认拷贝构造函数进行字节级复制,可能导致资源重复释放。例子展示了未正确实现拷贝构造函数时可能导致的无限递归。此外,文章提到了拷贝构造函数的常见应用场景,如函数参数、返回值和对象初始化,并指出类对象在赋值或作为函数参数时会隐式调用拷贝构造。
|
4月前
|
C++
c++进阶篇(一)——运算符重载
c++进阶篇(一)——运算符重载