本文已参与「新人创作礼」活动,一起开启掘金创作之路。
class template类模板
function template函数模板
这里说class可以换成typename,之后看下
这里函数模板不需要提前指明类型,有实参推导。模板块的编译,编译后也只是半成品,不能保证后头一定成功,后面使用的时候还会再编译一次。模板会写出.cpp或者.h,本身可以编译通过,但是真正使用时和应用程序本身还会再编译一次,那时候过不过就不确定了。
member template成员模板
比如在标准库,这种成员模板往往出现在模板的构造函数上
可以,反之不可以。
为了智能指针可以实现up-cast,所以构造函数要是member template
specialization,模板特化
特化就是:作为设计者,你可能某些独特的类型要做特殊设计
例如,如果设计了算法可以求两点之间连线的每个点的坐标,而有人告诉你如果数都是整数,可以有个非常快的算法。
partial specialization模板偏特化
个数的偏
参数不能跳着,例如对1,3,5绑定,这样不行
范围的偏
例如原本范围是所有类型,现在是指针类型,而指针的指向可以是任意类型,注意上下两个T不是一个,可以用不同符号
template template parameter模板模板参数
不太明白这个模板模板参数怎么写的,这里Container前的class不能换为typename,为什么
下面之所以错,是因为容器其实有第二模板参数,有的还有三,平常不写是有默认值,但是这里语法过不了,要通过需要加上中间那两句话,它是2.0新加的语法,由于不能短时间说完,这里不讲。
这里list已经指定了,list不再是模板了,所以不是模板模板参数
variadic template(since C++11)
这个新语法允许写任意个数的模板参数
auto(since C++11)
要会写原本ite的类型,auto只是为了方便,不过有一种情况很难写他的类型,也就是2.0里的lambda
ranged-base for(since C++11)
这里{}也是一个容器,2.0新特性
以前遍历容器用迭代器或者for each
这里第一个循环是传值,所以不能修改容器里的元素,而想要修改就要用第二种,传引用
reference
reference常见用途
类函数是否加const构成重载
Composition(复合)关系下的构造与析构
Inheritance(继承)关系下的构造与析构
Inheritance+Composition关系下的构造和析构
对象模型(Object Model):
关于vptr和vtbl
有虚函数的对象会多一个指针(虚指针),不论是几个虚函数都是一个指针,所以对象的大小多4个字节,那么这时之前说过的子类对象有父类的成分这句话同样没错,因为子类也有虚函数,他一定有虚函数,他继承了父类函数的调用权。
在C里面,如果调用函数,会使用call+地址的方式,解析后跳过去,执行完再回来,这叫做静态绑定。
如果调用虚函数,编译器知道不能做静态绑定,这是面向对象整个设计的关键点,做动态绑定。通过指针p找到vptr,找到vtbl(虚表),再从里面看看到底指向哪个函数。n是虚函数在虚表的位置,是编译时编译器看你放在第几个就是几
- 子类和父类返回值参数相同,函数名相同,有virtual关键字,则由对象的类型决定调用哪个函数。
- 子类和父类只要函数名相同,没有virtual关键字,则子类的对象没有办法调用到父类的同名函数,父类的同名函数被隐藏了,也可以强制调用父类的同名函数class::funtion_name。
- 子类和父类参数不同,函数名相同,有virtual关键字,则不存在多态性,子类的对象没有办法调用到父类的同名函数,父类的同名函数被隐藏了,也可以强制调用父类的同名函数class::funtion_name。
- 子类和父类返回值不同,参数相同,函数名相同,有virtual关键字,则编译出错error C2555编译器不允许函数名参数相同返回值不同的函数重载。
#include<iostream>
#include<list>
using namespace std;
class A
{
public:
void fun(){
std :: cout << "A"<<std :: endl;
}
A(){};
int c;
};
class B:public A
{
public:
void fun(){
std :: cout << "B"<<std :: endl;
}
B(){};
int c;
};
int main()
{
std::list<A*> l;
A a;
a.c = 4;
B b;
b.c = 6;
l.push_back(&a);
l.push_back(&b);
std::list<A*>::iterator i;
for( i = l.begin(); i != l.end();i ++)
{
(*i)->fun();//A A,这里up cast
// cout << (*i)->c <<endl;//4 4253600 ??
}
return 0;
}
只有虚函数才能实现多态,即如果是A调用A的fun,B调用B的fun(圆形调用圆形的draw,正方形调用他的draw)。上面虚函数调用的图就是虚机制,也就是动态绑定的形式。函数的这种用法就是多态。
关于this
关于Dynamic Binding
要满足动态绑定一定有三个条件:
- 指针
- 虚函数
- 向上转型
谈谈const
这里const只能放在成员函数后,全局函数后是不行的
我们使用的字符串是使用reference counting技巧做出来的,也就是说可以被共享,由于需要共享,所以如果改数据的话就需要COW。
在这里不能判断const是否是被const变量调用的,不知道是否该实现COW,所以又有了一个规则,就是上方灰色的。
关于new,delete
使用者直接用的new,delete都是表达式,表达式的行为是不能变的,不能重载的,也就是new和delete分解为三个和两个动作这个是不能变的,但是分解后的那个函数可以重载。
之前没说过class可以重载这些函数,它们用于内存管理,内存池的设计,这不是这里该讲的,这里讲的是重载的形式。
重载::operator new, ::operator delete,::operator new[],::operator delete[]
::表示全局的
new一定要有个参数大小,这些函数不是我们调用,我们调用的是表达式,表达式分成三个步,第一步就是这个,有重载就运行这个,没有就运行全局默认的,大小是编译器传进来的。delete要传指针。
重载member operator new/delete
接管后我们通常做内存池
重载member operator new[]/delete[]
示例,接口
示例
从这里可以看到如果new一个,他的size大小是12,但是new一个数组的大小不是元素个数*元素大小,还多了4,字节,他记录了有几个元素,这样才能知道调用几次构造和析构函数。
两个箭头表示的是构造和析构时this指针的移动方向
重载new(),delete()
有的人认为只有()里有指针的才叫placement new,因为指针定位在那里,所以名词才有意思