Effective C++ 读书笔记_1:构建全空对象数组/带参对象数组/Operator /Placement/new/指针Cast/分配一片内存

开始之前 :
一直被推荐了很久的effective c++ 直到今天终于开始看了,我会尽量认真做笔记而且尽快结束的,我有这本书的pdf中文版,如果有人恰好想要,留言我你的地址我会发给你。
10th - Nov


指针与引用 * -> &
指针:可以指向某些对象也可以不指向对象
引用: 不可以不指向对象,一定要指向某些内容

例一:指针与引用的初始化
  char* ptr = 0 / nullptr / NULL     -> 定义一个空指针  (正确)
   char*   ptr                       -> 未初始化的指针,没错但是不好
  char& ref = *ptr                    -> 1.指向空值的引用  (错误)
  string& ref                          -> 2.未被初始化的引用 (错误)

例二:普通对象的常量引用
classA ptr1;                            -> ptr1 是一个非 const 对象。
const  classA& ptr2 = ptr1;              -> 21的引用,但它是常量对象
  • 引用的特点:不存在空值引用->不需要检查->作为函数参数效率较高
  • 指针的特点:由于需要检测是否为空,因此效率不太高 -> if(ptr) …
  • 指针的特点:可以不停的变化指针所指的对象,但是引用的对象是无法改变的

CAST的功能
static_cast(表达式)

  1. const_cast
    在上面的例子中,一个针对普通对象展开的函数,对带常量引用就无法执行操作
想要套入函数就只能使用 const_cast   &ptr2  
注意这里的*&连用,因为不是直接将类型转换,而是间接将常引用的地址取出来以后  转换成普通类型对象的指针
- const_cast最常用的功能就是将常属性取出的作用

2.dynamic_cast
可以将指向基类的指针转换成指向子类的指针或者兄弟类的指针,非常实用
- 此外转换失败会返还出一个空指针或者直接报错

例一:   // 基类指针指向子类
A* ptr;
dynamic_cast(ptr)
在这里A是一个普通类,转换成了子类specialA / 此外注意这里的cast也是采用了指针转换的方式

例二: //  基类引用变成子类的引用
void function(specialA& a)
dynamic_cast(*a)
问题1:  混淆: 一个填写引用的地方就是直接填写对象进去就可以,当然单独生成引用填写也没问题
        混淆2: 一个填写基类引用的地方,填写子类引用需要进行转换
问题2:  不只是指针可以转换,引用也是可以转换的
  • 没有继承关系的两个类 是不能拿dynamic_cast的方式来进行转化的
  • 没有继承关系的可以用static_cast的方式转化

缺省构造函数
- 包含了非缺省构造函数的类,也可以包含缺省构造函数,然后根据需求决定使用哪一种
- 缺省构造函数有用: 构建类的数组的时候, 声明数组的时候,会默认先行调用缺省型构造函数
- 这里提到的缺省构造函数可以是1.全空型缺省 也可以是 2.带参型缺省
构建对象数组的使用方法如下:


堆型数组:     //大部分用到的利用new出来的只能这么来解决
classA a[10]          -> 错误 
classA a* = new A[10]    ->正确
非堆型数组:在有带参的情况下可以使用:
classA a[]= classA(参数1),classA(参数2),.......就可以这么声明了;


一个更为通用的方法:
typrdef classA* P                // 定义一种数据类型,叫做只能指向classA类的指针
P a[10];                         //  利用这个指针来定义数组是可以接受的
for (int i =0;i<10;i++){
a[i]=new classA(参数i)}           //  然后这个时候再来初始化每一个数据

重载别的可以,但是最好不要重载 && || 以及”,”

  • 原因是,++ += 等是没有优先计算顺序的,但是&&等符号除了拥有自身的含义外,还被定义上了优先计算顺序,你可以重新定义含义,但是优先计算顺序是无法改变的,所以可能会导致一些混乱的后果

New 操作符以及 Operator New的使用

/番外篇1. –>笔记 一个char指针是怎么被输出的?*/
/既可以是cout << ptr 也可以是cout <<*ptr都可以/

/番外篇2. –> 对象指针到底能不能在new的时候顺带赋值? /
/* 在一次简单测试中通过 A* ptr = new A(20) 测试的结果是有效的 */

/番外篇3. –>char的作用不仅仅是可以指向一个char 测试之后指向一个new char(“qwerty”)*/
/也是可以,有效的/

//刚刚又发现了一种新的快捷选中一段文章的方式 shift + 上下/PgUp/PgDn可以选中几行

  • 常见的new操作符的使用,例如char* ptr = new char(‘a’) 创建了一个新的内存指针
然而还有一种操作叫做operator new,相比于普通new而言,Operator new到底有什么用?
配合_FILE_ 与 _LINE_  表征new调用的位置

//使用案例A
1.首先于调用的cpp首定义 :    #define new new(__FILE__, __LINE__)
有了这个我们就可以调用错误发生的: 位置信息与文件信息
2.其次于类定义中,定义new的使用(毕竟new重载也是仅仅针对类成员而言的)
    void* operator new(size_t size, const char* file, int line)
    {
        std::cout<<"call A::operator new on file:"<"  line:"<std::endl;
        return malloc(size);
        return NULL;
    }
(这样我们就可以在甚至没有提到文件名称,行数信息的情况下使用它们了! 以后针对这个类的成员,new就有这样的功能)
3.调用:
        A* p1 = new A;   针对类A,调用new的时候实际使用的的是operator new,自动被附上行数以及文件名
4.函数的返回值类型是一个void* 类型,然后用A* 类型指针ptr来接收这个新的对象
//使用案例B
1.类定义中
    void* operator new(size_t size,string str)
    {
        cout<<"operator new size "<" with string "<return ::operator new(size);
    }
2.实际调用
    X *px = new("A new class") X;
// 以上两个案例的调用除了一个调用了行数/文件信息以外的区别是返回值以及传参方式的区别
 (1).针对文件/行数信息等,自动赋值的变量,在调用的时候不用刻意附上实参 
 (2).如果是想传入一个自定义的变量,比如某一条string,以new(para) A的方式传入,再定义他的使用方法
 (3).相同点都是针对类成员的时候才有的定义,而且都要用A*的方式接收这个void*指针

Placement new是一种特殊的operator new,但是operator new是无法被替换与重载的
假设我们手上已经有了一些现成的内存,这些内存现在永不到,我们想要使用这些现成的内存
1.同样的,谁能使用placement new? 只有类,才能定义/声明/使用placement new方法
2.首先我们得有一块现成的内存块,这个内存可以是缓冲区(buffer)也可以是stack上的一片内存
- 比如 char buf[100]
3.读到这里很困惑,我从那里给你变一片内存出来? 以下是内存的案例

    A* p = (A*)::operator new(sizeof(A)); //如果我们按照这种全手动的方式定义对象 

    new(p) A(); //手动定义内存+手动于内存上定义对象

    p->~A();   //析构  

    ########::operator delete(p); //释放  ######
    现在假设,我们手动分配了内存但是没有做最后一步,也就是不释放保留这一片内存
    现在我们就有一片内存了 =。=!

此时就可以调用placement new利用这一片内存了,pi = new (ptr) A 从ptr指向的内存片上我们定义出了A,并且用pi指向A ,利用A

你可能感兴趣的:(Effective C++ 读书笔记_1:构建全空对象数组/带参对象数组/Operator /Placement/new/指针Cast/分配一片内存)