C++基础之编写String类(拷贝构造函数)

来源:互联网 发布:分离音频的软件 编辑:程序博客网 时间:2024/05/16 08:52

要求:写个String类(这个类在std里是有的,我们要自己实现自己的机制)

String类里面主要的有四个函数:构造函数、拷贝构造函数、赋值函数、析构函数

我们通过写这些函数来理解什么是拷贝构造函数

class String{pbulic:char *m_data;}

构造函数:

要求:将字符串通过构造函数赋到成员指针中

String( char *str )//无返回值、参数是个char的指针{        int length = strlen(str);//看一下字符串的长度        m_data = new char[length + 1]//开辟字符串长度的空间,给‘\0’留个空间        strcpy(m_data,str);//将字符串拷贝到开辟的空间中}
解析:构造函数

构造函数我们很熟悉,不过这个构造函数中多了个参数,逻辑也简单


拷贝构造函数:初始化其他新建对象(假如说是有x,y两个对象,x对象初始化过,就是成员变量改赋值的都赋值了,y对象建立但什么都还没有赋值,通过拷贝构造函数用对象x来初始化对象y)

String( String & other)//和构造函数唯一的区别在于参数是个本类的对象{   int length = strlen(other.m_data);   m_data = new char[length + 1];   strcpy(m_data,other.m_data);}

解析:拷贝构造函数

通过上面可以看得出拷贝构造函数和构造函数的区别在于参数不同,这也是本质,是重载,特殊的一种拷贝构造函数而已

逻辑和构造函数中的相同,逻辑懂了怎么实现懂了,剩下的就是如何触发,怎么用

char *t = "1234向前走";String A(t);//调构造函数String B(A);//当你用本类的对象来初始化本类的对象的时候,调的是拷贝构造函数,不会调构造函数了(已经调过一种构造函数了,不会调两次构造函数了,拷贝构造也是构造)String B = A;//也可以这样来代替上一句 

赋值函数:将本类对象的值赋值给本类的另一个对象

Text & operator = ( Text & other){    m_data = other.m_data;    return other;}
解析:赋值函数

没有new空间是构造的时候已经开辟好了,只是没有赋值


析构函数:释放new出来的空间

~Text(){  delete[]m_data;//解析参考C++基础之delete和delete[]区别} 

类写好了,我们来总结的解析一下:

我们知道拷贝构造的本质,有点熟悉的感觉,惧怕它的感觉少了一半

我们知道怎么实现,还有一点惧怕,怕用不好

我们只要怎么用,不怕它了

深究难点一:深拷贝和浅拷贝

前提:我们只写了构造函数和析构函数调用时:char *s = "12345";        Text *p1 = new Text(s);        Text *p2 = new Text(*p1);        delete p1;        delete p2;问:结果会怎么样
解析:

图析:将new出来的空间delete两次造成堆损坏,delete p1后如果p2还要用new出来的这块空间造成野指针


造成这种结果的原因:

第二行,p1调用自己的拷贝构造函数

第三行,p2经p1来初始化,调用拷贝构造函数,我们没有写,如同普通构造函数一样,有默认的拷贝构造函数,看一下汇编里面是怎么样实现的


97后实现的默认的拷贝构造函数的功能,可以看得到,编译器这种默认的里面是四个mov的赋值汇编语句实现的是将t的值付给tt,如果我们这里一样,p1对象是谁,是成员指针,指针的值是字符串的地址,将字符串的地址给p2,也就是给第二个对象的成员指针里,有了图析的结果,这种就是浅拷贝

避免这种错误,我们得自己写拷贝构造函数,也就是深拷贝,也就是我们上面写的String的拷贝构造函数,在拷贝构造函数中new空间

将构造函数的声明写成private,只写个声明就可以了,这样我们如果用类的对象当值传入参数或返回或者帮类对象初始化都会编译不通过

深究难点二:拷贝构造函数的参数写法为什么用引用

看过之后明白拷贝构造函数要求的是参数是本类的对象,既然传对象,我可以有多种方式

(1)Text  other

  ( 2 ) Text *other

解析第一种:参数形式写成建对象的形式

首先,编译器不通过,是因为语法

其次,为什么这样不行,语法通过的话我是可以传入对象的


1.用本类对象来初始化本来对象,会调用拷贝构造函数

2.拷贝构造函数也是一种函数,调用之前在A这里压参

3.压的参是传入的对象的值(难点在这里)

4.想要找到传入对象的值得通过拷贝构造函数,所在又调用拷贝构造函数来寻找值……但是又通过拷贝构造函数来找……无限循环压参,在B的地方压参,B的上面压参,B的上面的上面压参(B本来是新建对象成员变量的空间)

所以不能用(1)

那我用(2),指针,尝试后编译器通过,可以使用,只不过是传入指针的操作比操作引用繁琐

发现一:


我在类里写了一个名为art的普通函数,参数是本类对象,对应汇编发现,在汇编指令001B1D2A的时候,call调一次函数,调的不是art,调的是什么,我们跳转过去看是拷贝构造函数,在汇编指令001B1D32的时候在调用art函数,由此我们得出结论

调用的普通函数,函数的参数是本类的对象时,先自动调用拷贝构造函数再调普通函数

而随后我发现一件事情,我的普通函数的返回值是本类的时候,不管调用普通函数的时候初始化本类其他对象的时候,或者函数中建立本类对象都会去掉拷贝构造函数,为什么,是因为建了对象,所以回去调拷贝构造,实际也就是取调的是构造函数,看参数是什么调的是哪一个,然后知道假如Text是类名,Text()或者Text(Text *p)或者Text(int m)或者是Text(Text &p)这四个函数,哪一个都可以是构造函数,哪一个也可以是用作拷贝构造函数而得出结论

拷贝构造函数就是构造函数

下面这道题几乎是笔试的必考题了~

编写String的构造函数、析构函数和赋值函数(25分)

已知类String的原型为:

class String

{

public:

String(const char *str = NULL); // 普通构造函数

String(const String &other); // 拷贝构造函数

~ String(void);   // 析构函数

String & operate =(const String &other); // 赋值函数

private:

char *m_data;  // 用于保存字符串

};

请编写 String 的上述 4 个函数。

完美解://构造函数

Sting::String(const char *str = NULL){

if(NULL == str)

{

m_data = new char[1];

m_data = '\0';

}else

{

int length = strlen(str);

m_data = new char[length+1];

strcpy(m_data,str);}

}

//拷贝构造函数

String::String(const String &other)

{ int length =strlen(other.m_data);

m_data =new char[length+1];

strcpy(m_data,other.m_data);

}

//赋值函数

String & operate =(const String &other)

{ if(this == other)

{return *this;}

else{

detele[]m_data;

int length = strlen(other.m_data);

m_data = new char[length+1];

strcpy(m_data,other.m_data);

return *this;}

}

//析构函数

~ String(void)

{detele[]m_data;}

(这道题我是在一个月内看过三遍到现在才有些感觉,看来我还是菜鸟级别的人物,今天自学了拷贝构造函数,没那么怕了,但也不能所有点都能自己问自己到底的回答出来,还要看这道题,反复的看 -----08-15)

(这道题现在看过来做起来很简单---09--25)

下面摘抄一段对这道题的评语:

能够准确无误地编写出String类的构造函数、拷贝构造函数、赋值函数和析构函数的面试者至少已经具备了C++基本功的60%以上!在这个类中包括了指针类成员变量m_data,当类中包括指针类成员变量时,一定要重载其拷贝构造函数、赋值函数和析构函数,这既是对C++程序员的基本要求,也是《Effective C++》中特别强调的条款。仔细学习这个类,特别注意加注释的得分点和加分点的意义,这样就具备了60%以上的C++基本功!

(PanPen120原创 如有建议 请留言)

0 0
原创粉丝点击