C++ 之构造函数(拷贝/复制构造函数)

构造函数

一、拷贝构造函数概念

拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于:

通过使用另一个同类型的对象来初始化新创建的对象。

复制对象把它作为参数传递给函数。

复制对象,并从函数返回这个对象。


二、引例

1、第一个问题

需求说明:
自定义Sting(字符串)类,以简化字符串的操作

//MyString.h
class String{
public:
	String(char * str = "");
	friend ostream & operator<<(ostream & os, const String & str);
	String & operator=(String & str);
	virtual ~String();
private:
	int length;			//字符串的实际长度(不包括\0)
	char * value;		//实际存储字符的字符数组
}
//main.cpp
String str1 = "爱喝可乐";
String str2;
str2=st1;

注意:
如果我们不重载赋值运算符,对象str1的内容直接复制到新对象str2中,对于没有指针的简单类来说这足够了,但当我们拥有作为数据成员的指针时,逐字节的复制将会把指针从一个对象复制给另一个对象,而两个指针就会同时指向一个内存

解决方案:
重载赋值运算符
(不知道如何重载运算符的同学可以看:C++ 之运算符重载)

String & String::operator=(String & str){
	delete[] value;		//首先释放字符串的默认空间
	length = strlen(str.value);	//重新测量字符串长度
	value = new char[lengh + 1];
	strcpy(value,str.value);
	return *this;
}

注意:
1、当重载赋值运算符时,务必确定一个对象中的所有数据成员都复制到另一个对象中。
2、如果包含多个数据成员,则每个成员都需要复制到目标对象中。

2、另一种场景

同一个类的对象初始化另一个对象时

Sting str1("爱喝可乐");
String str2(str1);

解决方案:

为类定义赋值/复制构造函数(即以对象为参数的构造函数)
一般格式:
	XXX::XXX(XXX & xxxptr)
使用const关键字可以保证复制过程中不会改变被复制的对象:
	XXX::XX(const XXX & XXXPtr)

在下面三种场景中会调用拷贝构造函数(复制构造函数):
1、当类的对象被初始化为同一类的另一个对象时
2、当对象被作为参数传递给一个函数时
3、当函数返回一个对象时


三、具体实例

自定义Sting(字符串)类,以简化字符串的操作

具体实现:
MyString.h

#ifndef MYSTRING_H
#define MYSTRING_H

#include
#include

using namespace std;

//自定义的字符串包装类

class MYString
{
    public:
        MYString();
        MYString(char * str);
        MYString(const MYString & str);//复制构造/拷贝构造
        virtual ~MYString();

        //重载赋值运算符,将数组中的每个元素都进行复制,而不是只复制数组指针
        const MYString & operator=(const MYString & str);

        friend ostream & operator<<(ostream & out, const MYString & str);

    protected:

    private:
        int m_length;       //字符串的实际长度 - 不包括\0
        char * m_value;     //实际存储字符的字符数组
};

#endif // MYSTRING_H

MySting.cpp

#include "MYString.h"
#include

MYString::MYString() : m_length(0)
{
    //char * str = "";    //长度为0,但是实际的字符数组中会存在唯一的元素:、0
    this->m_value = new char[1];
    this->m_value[0] = '\0';
}
MYString::MYString(char * str)
{
    //将传入的字符串str的值赋给当前对象中的m_value
    if(NULL==str)
    {
        this->m_value = new char[1];
        this->m_value[0] = '\0';
        return;
    }
    m_length = strlen(str);
    m_value = new char[m_length + 1];//为\0留出一个空间
    strcpy(m_value ,str);
}
MYString::MYString(const MYString & str)
{
    m_length = strlen(str.m_value);
    m_value = new char[m_length + 1];
    strcpy(m_value,str.m_value);
}

ostream & operator<<(ostream & out , const MYString & str)
{
    out<<str.m_value<<"\n";
    //out<<"m_value的长度:"<
    printf("%p",str.m_value);
    return out;
}
//当重载赋值运算符时,务必确定一个对象的所有数据都复制到另一个对象中(特别是有指针时)
//如果包含多个成员,那么每个成员都需要复制到内存对象中 - 深复制
//如果一个类拥有指针类型成员,那么大部分情况下,都需要使用深复制 - 才能将指针指向的内容复制一份出来,让原有的对象和新对象相互独立
//如果类的成员没有指针,那么一般浅复制足矣
const MYString & MYString::operator=(const MYString & str)
{
    if(this == &str)
        return *this;
    delete[] m_value;   //首先要释放字符串的原始空间
    m_length = str.m_length;
    m_value = new char[m_length + 1];
    strcpy(m_value,str.m_value);
    return *this;
}

MYString::~MYString()
{
    //析构时,释放字符数组所指向的空间
    delete[] m_value;
}

main.cpp

#include
#include"MYString.h"

using namespace std;

void TestString();

int main()
{
    TestString();
    return 0;
}

void TestString()
{
    MYString str1("abc");
    MYString str2 = "abcdefg";
    cout<<str1<<endl;
    cout<<str2<<endl;
    cout<<""<<endl;

    cout<<"对象之间的赋值:"<<endl;
    str1= str2;
    cout<<str1<<endl;
    cout<<str2<<endl;

    MYString str3("爱喝可乐");
    MYString str4(str3);
    cout<<str3<<endl;
    cout<<str4<<endl;
}


//打印结果:
/**
abc
00CE1248
abcdefg
00CE11A8

对象之间的赋值:
abcdefg
00CE10F8
abcdefg
00CE11A8
爱喝可乐
00CE0E28
爱喝可乐
00CE0E40

*/

四、小结

赋值/拷贝构造函数
对于简单的类,默认拷贝构造函数一般是够用的,没有必要再显式地定义一个功能类似地构造函数。
当类拥有其他资源时,如动态分配内存、打开文件、指向其他数据的指针、网络连接等,默认拷贝构造函数就不能拷贝这些资源,必须显式定义拷贝构造函数,以完整地拷贝对象地所有数据

你可能感兴趣的:(C++,c++)