C++零基础教程(拷贝构造函数)

简介: C++零基础教程(拷贝构造函数)

前言

本篇文章来讲解C++中的拷贝构造函数

一、拷贝构造函数概念

在C++中,拷贝构造函数(Copy Constructor)是一种特殊的构造函数,用于创建一个新对象并初始化其值为同一类别的另一个已有对象。拷贝构造函数接受一个引用参数,该引用参数是同一类别的另一个对象的引用。

拷贝构造函数通常用于以下情况:

对象的初始化:当使用一个已有对象来初始化一个新对象时,拷贝构造函数会被调用。这可以是通过直接赋值、传递对象给函数参数、返回对象等方式触发。

通过值传递参数:当将对象作为参数传递给函数时,拷贝构造函数会被调用来创建参数的副本以便在函数内部使用。

ClassName(const ClassName& other)
{
    // 拷贝构造函数的实现
}

如果没有显式定义拷贝构造函数,编译器会自动生成一个默认的拷贝构造函数。这个默认的拷贝构造函数会按照逐个拷贝成员变量的方式进行对象的拷贝。

示例代码:

#include <iostream>
using namespace std;
class Test
{
  int a;
  int b;
public:
  Test()
  {
    a = 1;
    b = 2;
    cout << "Test" << endl;
  }
  void GetVal(void)
  {
    cout << "a : " << a << endl;
    cout << "b : " << b << endl;
  }
};
int main(void)
{
  Test a;
  Test b = a;
  b.GetVal();
  return 0;
}

二、浅拷贝和深拷贝

浅拷贝(Shallow Copy)和深拷贝(Deep Copy)是关于对象拷贝的两个概念,常用于描述在进行对象复制时对于成员数据的处理方式。

浅拷贝是指将一个对象的值复制到另一个对象,包括对象的所有成员变量。当进行浅拷贝时,如果对象中包含指针类型的成员变量,那么只是复制指针的值,而不是创建新的独立副本,这意味着原始对象和新对象将共享相同的数据资源。这样,当其中一个对象修改了共享数据时,另一个对象的数据也会受到影响。

相比之下,深拷贝是指创建一个新对象,并将原始对象的所有成员变量的值复制到新对象,包括动态分配的资源。这样,每个对象都拥有自己的独立副本,修改一个对象的数据不会影响其他对象。

三、什么时候需要使用深拷贝

1.当一个类包含指针成员,并且在拷贝对象时需要创建该指针成员的独立副本时。如果使用浅拷贝,多个对象将共享同一个指针,这可能导致意外的行为和资源管理问题。通过使用深拷贝,每个对象都拥有自己的独立副本,从而避免了这些问题。

2.当一个类拥有资源管理的责任,例如动态分配的内存、文件句柄等,并希望在拷贝对象时创建这些资源的独立副本,以确保每个对象可以独立管理和释放这些资源。这样可以避免多个对象之间的资源冲突或资源泄漏。

3.当一个类是一个容器类,它包含其他对象作为其成员,并且在拷贝容器对象时需要递归地拷贝容器内的所有成员对象。这确保了深层嵌套的对象都具有独立的副本,而不是共享相同的引用。

错误案例:

这里类包含指针成员,没有使用深拷贝,导致了多次对指针变量P进行释放,所以会导致程序的崩溃。

#include <iostream>
using namespace std;
class Test
{
  int* p;
public:
  Test()
  {
    p = new int;
    *p = 1;
    cout << "Test" << endl;
  }
  Test(const Test& obj)
  {
    p = new int;
  }
  void GetVal(void)
  {
    cout << *p << endl;
  }
  void SetVal(int val)
  {
    *p = val;
  }
  ~Test()
  {
    delete p;
  }
};
int main(void)
{
  Test a;
  Test b = a;
  a.GetVal();
  b.SetVal(10);
  b.GetVal();
  a.GetVal();
  return 0;
}

正确做法:

这里应该使用深拷贝,创建一个独立的指针副本出来,这样就不会产生多次释放内存的错误了。

#include <iostream>
using namespace std;
class Test
{
  int* p;
public:
  Test()
  {
    p = new int;
    *p = 1;
    cout << "Test" << endl;
  }
  Test(const Test& obj)
  {
    p = new int;
  }
  void GetVal(void)
  {
    cout << *p << endl;
  }
  void SetVal(int val)
  {
    *p = val;
  }
  ~Test()
  {
    delete p;
  }
};
int main(void)
{
  Test a;
  Test b = a;
  a.GetVal();
  b.SetVal(10);
  b.GetVal();
  a.GetVal();
  return 0;
}

四、拷贝构造函数和赋值的区别

用途:

1.拷贝构造函数:用于在创建一个新对象时以另一个同类对象进行初始化。

赋值操作符:用于将一个已存在的对象的值赋给另一个已存在的对象。

语法:

2.拷贝构造函数:拷贝构造函数是构造函数的一种特殊形式,其函数名与类名相同,没有返回类型,在函数参数中接受一个同类对象的引用作为参数。

赋值操作符:赋值操作符是类的成员函数,名称为 operator=,其中 operator 是一个关键字,可以重载为类的成员函数,用于对同类对象进行赋值。

调用时机:

3.拷贝构造函数:当使用一个对象去初始化另一个对象时,会调用拷贝构造函数。例如,通过直接声明或传递参数创建对象,或通过值传递方式将对象传递给函数等。

4.赋值操作符:赋值操作符用于将已存在的对象的值赋给另一个已存在的对象。例如,通过赋值语句将一个对象的值赋给另一个对象。

工作方式:

5.拷贝构造函数:拷贝构造函数通过创建一个新对象,并将源对象的成员变量值复制到新对象中来完成对象的拷贝。通常,在拷贝构造函数中,还需要对动态分配的资源(例如内存)执行适当的深拷贝操作,以确保新对象具有独立的资源副本。

赋值操作符:赋值操作符在已存在的对象上修改其成员变量的值,以匹配另一个对象的值。这可能涉及释放原始对象持有的资源,并分配新的资源来保存来自另一个对象的值。

返回类型:

拷贝构造函数:拷贝构造函数没有返回类型,因为它的作用是用于创建新的对象,而不是返回任何值。

赋值操作符:赋值操作符的返回类型通常是指向当前对象的引用(即 T&)或返回一个新的对象副本(即 T)。返回引用类型的赋值操作符允许支持连续赋值的链式语法。

总结

本篇文章就讲解到这里,这一块的知识还是非常重要的大家务必要掌握好。


相关文章
|
2月前
|
算法 数据挖掘 Shell
「毅硕|生信教程」 micromamba:mamba的C++实现,超越conda
还在为生信软件的安装配置而烦恼?micromamba(micromamba是mamba包管理器的小型版本,采用C++实现,具有mamba的核心功能,且体积更小,可以脱离conda独立运行,更易于部署)帮你解决!
66 1
|
2月前
|
存储 C++
c++的指针完整教程
本文提供了一个全面的C++指针教程,包括指针的声明与初始化、访问指针指向的值、指针运算、指针与函数的关系、动态内存分配,以及不同类型指针(如一级指针、二级指针、整型指针、字符指针、数组指针、函数指针、成员指针、void指针)的介绍,还提到了不同位数机器上指针大小的差异。
44 1
|
2月前
|
Linux C语言 C++
vsCode远程执行c和c++代码并操控linux服务器完整教程
这篇文章提供了一个完整的教程,介绍如何在Visual Studio Code中配置和使用插件来远程执行C和C++代码,并操控Linux服务器,包括安装VSCode、安装插件、配置插件、配置编译工具、升级glibc和编写代码进行调试的步骤。
280 0
vsCode远程执行c和c++代码并操控linux服务器完整教程
|
6月前
|
存储 编译器 C++
C++进阶之路:何为拷贝构造函数,深入理解浅拷贝与深拷贝(类与对象_中篇)
C++进阶之路:何为拷贝构造函数,深入理解浅拷贝与深拷贝(类与对象_中篇)
53 0
|
6月前
|
C++ 容器
【C++】拷贝构造函数、拷贝赋值函数与析构函数
【C++】拷贝构造函数、拷贝赋值函数与析构函数
120 6
|
6月前
|
存储 编译器 C++
【C++】:拷贝构造函数和赋值运算符重载
【C++】:拷贝构造函数和赋值运算符重载
30 1
|
6月前
|
存储 编译器 C++
【C++】类和对象③(类的默认成员函数:拷贝构造函数)
本文探讨了C++中拷贝构造函数和赋值运算符重载的重要性。拷贝构造函数用于创建与已有对象相同的新对象,尤其在类涉及资源管理时需谨慎处理,以防止浅拷贝导致的问题。默认拷贝构造函数进行字节级复制,可能导致资源重复释放。例子展示了未正确实现拷贝构造函数时可能导致的无限递归。此外,文章提到了拷贝构造函数的常见应用场景,如函数参数、返回值和对象初始化,并指出类对象在赋值或作为函数参数时会隐式调用拷贝构造。
|
6月前
|
存储 算法 编译器
C++ 函数式编程教程
C++ 函数式编程学习
|
6月前
|
存储 编译器 开发工具
C++语言教程分享
C++语言教程分享
|
6月前
|
存储 编译器 C++