自学内容网 自学内容网

C++拷贝构造函数

拷贝 指的是复制数据,复制内存。
在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)


原文地址:https://blog.csdn.net/hdfinder/article/details/142386015

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!