A.导读
泛型编程和面向对象编程虽然分层不同思维,但是它们正式C++的技术主线。随意我们也要讨论模版。
test.cpp
一些书籍:Effective C++ The C++ Standard Library.
标准库除了去使用他非常方便的,但如果能理解它更好,所以有STL源码解析、标准库里基本不是面向对象编程的思维,是泛型编程的思维。用了很多模版,不是用继承写的。同样的一件事情如果分成很多class的话会有很多继承,整个标准库的技术不是用面向对象的思维。C++编译器。
B.Conversion Function
转换函数。
operator double() const{
return (double) (m_numerator/ m_denominator);}
C.non-explicit-one-argument
explicit-one-argument ctor
Fraction f(3, 5)
Fraction d2 = f+4//调用non-explicit ctor将4转为Fraction(4. 1), 然后调用operatro+
conversion functions vs non-explicit-one-argument ctor
但如果此时还有operator double(),第二行会报错,ambiguous。
加了explicit后d2 = f+4 4就不会变成fraction
explicit 百分之九十用在构造函数前面
proxy 用另一种类去代理真正要传回的值。转换函数,有从这边的转到那边的,也有从那边的转到这边的,两个方向。
D.pointer-like classes关于智能指针
E.function-like classes
所谓的仿函数。
他们对操作符()进行重载,至于小括号里面做什么要看设计。
template
struct pair{
T1 first;
T2 second;
pair(): first(T1()), second(T2()) {}
pair(const T1& a, const T2& b)
: first(a), second(b) {}
}
发现标准库中的其他仿函数都继承了binary_function和unary_function
这两个是仿函数使用的base classes,所以标准库里有很多仿函数,他们都重载了(),并继承了一些奇怪的父类。
F.namespace经验谈
把自己的变量和类名等等用自己的namespace包装起来
int main (int argh, char** argv){
jj01::test_member_template();
j02::test_template_template_param();
}
G.class template类模板
之前已经看到过,模板在类的外面,类内要用到的地方都用T来代替。
H.函数模板
概念完全一样,写在函数的前面
template
inline
const T& min(const T& a, const T& b){
return b
}
编译器会对函数模板进行实参的推导(argumentt deduction),所以函数模板在编译的时候并不能确定,当被使用的时候会再一次确定。
I.成员模板
成员模板一样,声明放在成员的外面
template
pair(const pair
& p) :first(p.fist), second(p.second) {}
很多构造函数中会用到成员模板。比如在只能指针中
template
class shared_ptr:public __shared_ptr <_Tp>{
...
template
explicit shared_ptr(_Tp1* __p)
:__shared_ptr<_Tp>(__p) {}
...
}
J.模板特化 specialization
特化的反面是泛化,可以理解为当我们使用模板的时候如果出现了在某几种情况下我们需要进行特殊处理时,可以进行模板特化。
template
struct hash {};
template<>
struct hash
{ size_t operator() (int x) const {return x;}
}
K.偏特化
分为两种,一种是个数上的偏,比如模板参数有两个,但是在特化的时候我们只在某一个值绑定的时候就要生效,这个叫偏特化,另一种是范围的偏特化,比如一开始的时候我们的模板可以允许任何类型,后来我们要把范围缩小为只允许指针,这个叫范围的偏特化,不要把下面模板中的参数和上面模板中的混淆。
L.模板模板参数
本身是一个模板参数,在模板里面又有模板,关于typename和class的共通问题,只有在template
template
template
class Container
>
由于容器传入其实不止一个参数,会出问题。
以下不是模板模板参数
template
> class stack{}
因为后者已经完全确定了。
M.关于C++标准库
最关键的是容器和算法。
接下来是C++2.0的几个关键简单的部分,variadic template, auto和range-base for loop
N.三个主题
variadic templates
void print(){}
template
void print(const T& firstArg, const Types&... args){
cout <
print(args...);
}
把输入的模板和输入的变量分为一个和一包,然后利用迭代把所有不同类型的数据同时处理。
如print(7.5, "hello", bites<16>(377), 42)
所以上面一个print()是必须的,作为一个迭代循环的出口。
如果你想知道后面一包里面有几个,可以使用sizeof...(args)
auto
使用auto的时候肯定有右边的等式,作为一个语法行,不能空定义。
auto ite = find(c.begin(), c.end(), target);
编译器会帮你推。
range-base for
for(dec1: coll) {}
冒号后面一定是一个容器,把容器中的每一个元素设定为冒号前的变量
vector
vec; for(auto elem:vec){
cout << elem << endl;
}
这样的写法比遍历一个容器以前的写法简单很多,以前要用迭代器的循环或者foreach
上面这样的写法是一个copy动作,不能改变容器中的值,但传引用可以改值
for (auto& elem : vec){
elem *=3;
}
O.reference
当用引用赋值过后,两个东西就完全相同了,只是名字不同。如
int x = 0;
int*p = &x;
int& r = x;//r代表x,现在r和x都是0
int x2 = 5;
r = x2;//r不能重新代表其他物体,现在r和x都是5
int& r2 = r;
object和其reference大小相同,地址也相同,java中所有的变量都是reference。
很少声明一个变量是reference,一般用在传参数中。
void func1(cls* pobj){obj->xxx();}
void func2(cls obj) {obj.xxx();}
void func3(cls& obj) {obj.xxx();}
func1(&obj);//接口不同
func2(obj);
func3(obj);
当两个函数的传参只有reference不一样时,被视为相同的声明,加了const以后两个函数的签名被视为不相同。
这次的作业涉及到了对象在内存里是如何存在的,让我学到了一个对象中的对齐规则,在一个对象中以最长的数据类型大小为对齐规则,当一个类是虚拟类的时候,它会多一个虚拟指针用来指向它的虚拟表。数据的顺序也会影响对象的大小,当两个数据加起来小于等于对齐的大小时,这两个数据会放在连续位置。当子类是多重继承的时候,会有一个指针指向多个虚拟指针用来指向多个虚拟表。另外在使用gdb调试器的时候,也学会了很多新的命令使用,比如set p pretty on和set p obj on,第一条可以让结构的显示按照数据类型分段显示,后一个可以显示对象类型的数据。