开始之前 :
一直被推荐了很久的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; -> 2是1的引用,但它是常量对象
CAST的功能
static_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: 不只是指针可以转换,引用也是可以转换的
缺省构造函数
- 包含了非缺省构造函数的类,也可以包含缺省构造函数,然后根据需求决定使用哪一种
- 缺省构造函数有用: 构建类的数组的时候, 声明数组的时候,会默认先行调用缺省型构造函数
- 这里提到的缺省构造函数可以是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可以选中几行
然而还有一种操作叫做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