C++拷贝构造函数

devtools/2024/9/25 0:34:19/

拷贝 指的是复制数据,复制内存。
在C++中,要避免不必要的复制。当把一个对象或变量,一段数据从一个地方复制到另一个地方的时候,我们实际上会拥有两个副本。在程序运行过程中分配的内存大小是有限的,大量的复制势必会造成不必要的占用内存,并且消耗计算资源。

一个简单的例子

int a = 5;
int b = a;

这里实际上是创建了一个a的副本,a和b是两个独立的变量,它们有不同的内存地址。所以如果我们把b改为3,a仍然是2。在类中,也是同样的道理。复制指针就不一样了,指向的内容是同一个,修改b也会影响a。
指针赋值
指向的内容是同一个,修改b也会影响a。

struct Vector2{float x,y;
};
int main()
{Vector2* a = new Vector2();Vector2* b = a;b->x = 5;std::cout<<a->x<<','<<a->y<<std::endl; std::cin.get();
}

除了引用外,每当你编写一个变量被赋值给另一个变量的代码时,你总是在复制。

浅拷贝

class String
{
private:char* m_Buffer;//字符缓冲区指针 unsigned int m_Size;//字符串的大小
public:String(const char* string){m_Size = strlen(string);m_Buffer = new char[m_Size];//for(int i=0;i<m_Size;i++)//	m_Buffer[i]=string[i];//memcpy(m_Buffer,string,m_Size);strcpy(m_Buffer,string);} //友元函数,外部函数可以访问私有成员 friend std::ostream& operator<<(std::ostream& stream, const String& string); //在析构函数中释放内存资源(new char 需要被释放) ~String(){delete[] m_Buffer;}
}; std::ostream& operator<<(std::ostream& stream, const String& string)
{stream<<string.m_Buffer;return stream;
}//重载运算符,将String对象输出到流std::ostream中int main()
{String string = "hdfiner";//浅拷贝,将字符串指针赋值给second。并不是重新开辟一片新的内存来存储数据String second = string;std::cout<< string << std::endl;std::cout<< second << std::endl;std::cin.get();
} 

这里给出了一个String类,其中包含字符指针m_Buffer,长度m_Size。并且在析构函数中释放字符指针所指向的字符数组。这里程序运行会出错,因为相同的内存会有被释放两次的风险。

深拷贝(使用拷贝构造函数)

浅拷贝不会到指针所指的内存中去复制内容。这里我们希望second对象能够拥有自己独立的内存,复制整个对象。因此,我们需要使用拷贝构造函数来实现深拷贝。
在 C++ 中,拷贝构造函数(copy constructor)是用于初始化一个对象为同类的另一个对象的特殊构造函数。拷贝构造函数是一个构造函数,当你复制第二个字符串时,它会被调用。当你把一个字符串赋值给一个也是字符串的对象时,(当你试图创建一个新的变量并给它分配另一个变量,而这个变量和你正在创建的变量类型相同时),你复制这个变量,也就是所谓的拷贝构造函数。它通常在以下几种情况下被调用:

  • 当用一个对象来初始化另一个同类型对象时。
  • 当对象作为参数传递给函数时(按值传递)。
  • 当对象作为返回值从函数中返回时(按值返回)。
  • 当对象被抛出或者捕获时。
    C++中提供的默认拷贝构造函数
class ClassName {
public:ClassName(const ClassName& other) {// 通过 other 对象初始化当前对象}
};
//如果我们决定不需要拷贝构造函数,不允许复制,我们可以将这个拷贝函数声明为delete :
ClassName(const ClassName& other) = delete;
class String
{
public:char* m_Buffer;//字符缓冲区指针 unsigned int m_Size;//字符串的大小
public:String(const char* string){m_Size = strlen(string);m_Buffer = new char[m_Size];//for(int i=0;i<m_Size;i++)//	m_Buffer[i]=string[i];//memcpy(m_Buffer,string,m_Size);strcpy(m_Buffer,string);std::cout<<"Constructor called"<<std::endl;} //拷贝构造函数,分配内存空间,复制字符串String(const String& other): m_Size(other.m_Size){m_Buffer = new char[m_Size+1];strcpy(m_Buffer,other.m_Buffer);std::cout<<"Copy Constructor called"<<std::endl;}//友元函数,外部函数可以访问私有成员 friend std::ostream& operator<<(std::ostream& stream, const String& string); //在析构函数中释放内存资源(new char 需要被释放) ~String(){delete[] m_Buffer;std::cout<<"Destructor called"<<std::endl;}
}; std::ostream& operator<<(std::ostream& stream, const String& string)
{stream<<string.m_Buffer;return stream;
}//重载运算符,将String对象输出到流std::ostream中int main()
{String string = "hdfiner";String second = string;string.m_Buffer[4]='5'; std::cout<< string << std::endl;std::cout<< second << std::endl;std::cin.get();
} 

避免不必要的复制

void PrintString(String string)
{String copy = string;std::cout << string << std::endl;
}

上面的函数会造成参数赋值(传值),建议使用const引用传递对象,因为你可以在你写的的函数的内部决定是否要复制,总是赋值会严重影响程序运行效率。当你传递字符串的时候,不管这个字符串是你自己的String类还是标准库里的String(std::string),总是要通过const引用来传递。即:
void PrintString(const String& string)


http://www.ppmy.cn/devtools/116734.html

相关文章

springboot系列--web相关知识探索一

一、web知识探索概述 一、探索大纲 二、SpringMVC原理流程图 二、springmvc自动配置 springboot在底层自动帮我们配置好了mvc所需要的各个组件。当然&#xff0c;我们也可以自己定制化相关组件。 可参考官方文档&#xff1a; 三、静态资源探究 一、静态资源存放位置 静态资…

vue和thinkphp路由伪静态配置

vue路由伪静态配置&#xff1a; location / { try_files $uri $uri/ /index.html; } thinkphp 路由伪静态配置 location ~* (runtime|application)/{ return 403; } location / { if (!-e $request_filename){ rewrite ^(.*)$ /index.php?s$1 last; break; } }

「DAOI R1」Magic

「DAOI R1」Magic 题目背景 -1,-1,2 题目描述 乔木 来到了大魔王的面前&#xff0c;他决定使用魔法击败魔王。 给定一个整数 n n n&#xff0c;表示有 n n n 个魔法阵&#xff0c;在每个魔法阵上都存在着一定的魔力值 a i a_i ai​。 你每次可以选择三个魔法阵 i , j ,…

Maya---机械模型制作

材质效果&#xff08;4&#xff09;_哔哩哔哩_bilibili 三角面 四边面 多边面 *游戏允许出现三角面和四边面 游戏中一般是低模&#xff08;几千个面&#xff09; 动漫及影视是高模 机械由单独零件组合而成&#xff0c;需独立制作 低面模型到高面模型 卡线是为了将模型保…

Qt-拖放

概述 拖放提供了一种简单的可视化机制&#xff0c;用户可以使用它在应用程序之间和应用程序内部传输信息。拖放功能类似于剪贴板的剪切和粘贴机制。 本文档描述了基本的拖放机制&#xff0c;并概述了在自定义控件中启用它的方法。Qt的许多控件也支持拖放操作&#xff0c;如it…

element-ui多个消息提示只显示最后一个

在使用 Element UI 的 Message 消息提示时&#xff0c;默认情况下&#xff0c;如果你连续调用多个 Message 方法&#xff0c;它们会依次显示&#xff0c;直到用户关闭或它们自动消失。但是&#xff0c;如果你希望只保留最后一个消息提示&#xff0c;即每当新消息出现时&#xf…

算法【Java】—— 位运算

位运算总结 位运算的运算符&#xff1a;按位与&#xff08;&&#xff09;&#xff0c;按位或&#xff08;|&#xff09;&#xff0c;按位异或&#xff08;^&#xff09;&#xff0c;按位取反&#xff08;~&#xff09;&#xff0c;还有移位操作符 <<&#xff0c;>…

WEB攻防-JavaWweb项目JWT身份攻击组件安全访问控制

知识点&#xff1a; 1、JavaWeb常见安全及代码逻辑&#xff1b; 2、目录遍历&身份验证&逻辑&JWT&#xff1b; 3、访问控制&安全组件&越权&三方组件&#xff1b; 演示案例&#xff1a; JavaWeb-WebGoat8靶场搭建使用 安全问题-目录遍历&身份认…