剑指offer 读书笔记——第2章
感觉自己的代码能力还在很弱,所以又第二遍仔细的阅读剑指offer了
第二章 面试需要的基础知识
面试题1 赋值运算函数
所谓赋值运算函数就是对=这个操作符进行重载,从而使等号能够直接用于两个实例之间的赋值。这个C++课上学过,不过现在忘完了,只记得学过。
经典解法
需要考虑的点
- 返回值的类型声明为该类型的引用,在函数结束前返回实例自身的引用(*this)。只有返回一个引用,才可以连续赋值。
object1=object2=object3
就是连续赋值。赋值采用右结合律,从最右边开始计算。不然的话当首先执行完object2=object3
后,object2
虽然自己的成员已经得到了更改,但是它不能再作为object1=object2
这个等号的右值,因为在前一个=的执行中,它没有获得返回值。而返回对象的话,就需要重新执行新建一个对象和销毁对象的构造与析构操作,增加不必要开销,降低赋值函数的效率。 - 传入的参数申明为常量引用
- 释放实例自身已有的内存
- 判断传入参数是否和当前的实例相同,相同的话不进行赋值操作,直接返回实例。
代码:初级版
CMyString& CMyString::operator = (const CMyString& str)
{
if(this == &str)
return *this;
delete []m_pData;
m_pData = nullptr;
m_pData = new char[strlen(str.m_pData) + 1]; //给strcpy复制的字符串申请空间
strcpy(m_pData, str.m_pData);
return *this;
}
代码:高级版
CMyString& CMyString::operator = (const CMyString& str)
{
if(this != &str){
CMyString strTemp(str); // 新建一个实例
char *pTemp=strTemp.m_pData; // 新建一个临时指针保存数据
strTemp.m_pData = m_pData; // 交换
m_pData = pTemp;
}
return *this;
}
初级版在使用new分配内存的时候,已经把原来的数据给清楚了(delete []m_pData;
),这时在新分配内存的时候,如果内存不足会导致new char抛出异常,使得m_pData 变成一个空指针,这样很容易导致程序崩溃。因为在抛出异常之后,原来的CMyString实例因为数据被delete了,所以不再保持有效的状态,违背了异常安全性的原则。 (可以先将原来的数据备份,等new 成功了,再将原来的数据delete给释放掉)
代码思路:新建一个实例,新建一个临时指针从而用于数据交换。
tmp tmp
^ |
|
str<--this
先将str的值备份到tmp中,然后将str的值更新为this的值,然后在把this的值更新为tmp的值
两个数据的交换必然需要新建第三个变量来存放其中一个变量的值。
之所以需要新建一个实例是因为实例是个临时变量,所以完成操作后会自动释放掉他的数据,也就是原来的数据。
指针与引用的异同
常量指针与常量引用。
对于引用只有两种描述:
// 对常量类型的引用
const type &ref = type a;
//对变量类型的引用
type &ref = type a;
对常量类型的引用 实际上既可以对常量类型引用,也可以对变量类型引用,但是不能通过引用来修改它引用的对象。
对于指针的修饰有4种:
// 普通的指针 poi is a pointer point to a type ,
//指针的值(指向的地址,可以改变),被指向的变量的值也可以改变
type *poi;
//常量指针 poi is a const pointer point to a type
//指针的值(指向的地址)不能改变,被指向的变量的值可以改变
type *const poi;
// poi is a pointer point to a const type
//指针的值(指向的地址)可以改变,所以指针可以指向其他变量,
//被指向的变量的值不可以改变,所以指针不能修改指向的变量的值,
const type *poi;
// poi is a const pointer point to a const type
// 指针的值(指向的地址)不能改变,所以指针不能指向其他变量,
//被指向的变量的不可以改变,所以指针不能修改指向的变量的值
const type *const poi;
以* &作为分割符进行读取,const作为前缀修饰的对象,对象存储的值不变。
对于指向const type 的指针或者引用而言,指向的(引用的)变量类型不一定是常量,但是无法通过指针或者引用来修改其指向的(引用的)变量的值。《C++primer》中所说:所谓指向常量的指针或引用,不过是指针自以为自己指向了一个常量,所以自觉地不去改变所指对象的值。
疑惑
- 为什么申明的返回值是引用,而返回的却是指针?
- 引用是地址的别名,但是不分配内存空间。
CMyString &A= *this
这里*this是指向CMyString这个类的实例的地址,而A是CMyString的一个实例的引用,是引用就需要与这个实例的内存地址相关联,所以可以传入 *this ,也就是实例的地址。 本来 =号用于赋值的时候,左值是地址,右值是变量地址上的数据也就是变量的值,但是引用的=号的右值是变量的地址。所以可以传入地址,也就是指针。
- 引用是地址的别名,但是不分配内存空间。
- 传入的参数是个常量引用,为什么不使用常量指针:
const CMyString *str
- 虽然
const CMyString *str
和congst CMyString &str
都不能去修改实参的值了,但是引用不是对象,是不分配内存的啊,使用指针的话,肯定会占用内存,然后不用的时候销毁内存,所以效率会降低。而且引用是一定不会空的,所以不用考虑所指的对象是否为空。(提升效率的关键,可以不申请内存就不申请内存,因为不管是自己去释放,还是系统自动回收,分配和回收都会带来效率的下降。所以能用指针就用指针,能用引用就用引用,最坏的情况才是值拷贝。)( 对象是指一块能存储数据并具有某种类型的内存空间)
- 虽然