1.转换函数
class Fraction
{
public:
explicit Fraction(int num, int den=1)
: m_numerator(num), m_denominator(den)
{ cout << m_numerator << ' ' << m_denominator << endl; }
operator double() const {
return (double)m_numerator / m_denominator;
}
Fraction operator+(const Fraction& f) {
cout << "operator+(): " << f.m_numerator << ' ' << f.m_denominator << endl;
//... plus
return f;
}
标黄的即为转换函数,函数名称为要转换的类型,无参数无返回值,且不改变成员数据,所以用const修饰函数
double d = 4 + f,编译器首先查找是否重载操作符+,若没有则看f是否有转换函数,若有则可以编译通过
2.non-explicit-one-argument ctor
蓝色段表示一个实参就够了,因为第二个实参有默认值
Fraction d2 = f+4; 调用non-explicit ctor 将4转为Fraction,然后调用operator+
如果non-explicit-one-argument ctor与转换函数并存时,Fraction d2=f+4;会出错,会造成编译器困惑,可以是Fraction与Fraction相加,也可以是Double与Double相加,因此要保证唯一性,不能有二义性。
在构造函数前加explicit,则无法将double转换为Fraction,一般只在构造函数前用
3 pointer-like classes
3.1 智能指针
template<class T>
class shared_ptr
{
public:
T& operator*() const
{ return *px; }
T* operator->() const
{ return px;}
shared_ptr(T* p) : px(p) { }
private:
T* px;
long* pn;
}
这种类要有指针的基本功能,首先可以用普通指针来初始化,其次要重载操作符* 和 ->
->有个特殊的地方,->用掉之后还会继续作用下去,所以sp->method(); 可以写成px->method();
3.2迭代器
主要用途是遍历容器,迭代器也是pointer-like classes,需要额外重载很多操作符,比如++、--等
reference operator*() const
{ return (*node).data; }
pointer operator->() const
{ return &(operator*()); }
//->的重载中调用操作符*的重载
//即return的是&((*node).data)
使用示例
list<Foo>::iterator ite;
...
*ite;// 获取一个Foo对象
ite->method();
//意思是调用Foo::method
//相当于(*ite).method
//相当于(&(*ite))->method
4.funtion-like classes,所谓仿函数
能够接受()的东西则称之为函数,或者像函数的类
template<class T>
struct identity :public unary_function<T,T> {
const T&;
operator() (const T& x) const { return x; }
}
function-like classes 都重载了()操作符,都会继承奇特的base classes,unary_funtion 和 binary_function
5.模板
5.1 类模板
template<typename T>
class complex
{
public:
complex (T r = 0, T i = 0)
: re(r),im(i) { }
private:
T re, im;
}
使用方法
complex<double> c1(2.5,1.5);
complex<int> c2(2,6);
5.2 函数模板
template <class T>
inline
const T& min(const T& a, const T& b)
{
return b < a? b : a;
}
编译器会对function template进行实参推导,编译器无需知道T的具体类型
5.3成员模板
template <class T1, class T2>
struct pair {
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair() : first(T1()), second(T2()) {}
pair(const T1& a, const T2& b) : first(a), second(b) {}
template <class U1, class U2>
pair(const pair<U1, U2>& p) : first(p.first), second(p.second) {}
};
黄色段落即为成员模板,常见于标准库中类的构造函数,为的是让构造函数更有弹性
5.4 specialization 模板特化
面对某些独特的类型要做特殊的设计时需要用到模板特化
模板泛化
template <class Key>
struct hash { };
这种情况下,key可以是任意的class类型,因此称之为泛化
模板特化
template<>
struct hash<char>{
size_t operator() (char x) const {return x;}
};
如果<>中不是char,则调用泛化,若类型为特化中的已经指定的类型,则调用特化的内容
5.5.模板偏特化
偏特化就是如果这个模板有多个类型,那么只限定其中的一部分。包括个数上的偏以及范围上的偏
个数上的偏
template<typename T, typaname Alloc=.......>
class vector
{
...
}
template<typename Alloc=......>
class vector<bool,Alloc>
可以绑定不定个数的模板
范围的偏
template <typename T>
class C
{
...
};
template<typename T>
class C<T*>
{
...
}
上述即为范围的偏,从任意范围缩小为必须是指针,但指针可以指向任意类型
5.6 模板模板参数
template<typename T,
template <typename T>
class Container>
>
第二个参数为模板参数
5.7 c++11的三个主题
5.7.1 variadic templates 数量不定的模板参数
允许写任意个数的模板
template<typename T,typename... Types>
void print(const T& firstArg,const Types&... args)
{
cout<<firstArg<<endl;
print(args...);
}
接受两组参数,第一组firstArg,第二组为args数量不定,分为一个和一包(用...表示)
...就是一个所谓的pack(包)
用于template parameters,就是template parameters pack(模板参数包)
用于function parameter types,就是function parameter types pack(函数参数类型包)
用于function parameters,就是function parameters pack(函数参数包)
5.7.2 auto
list<string> c;
...
list<string>::iterator ite;
ite = find(c.begin(),c.end(),target);
可改为
list<string> c;
...
auto ite = find(c.begin(),c.end(),target);
auto为编译器自动推导类型
错误用法
list<string> c;
...
auto ite;
ite = find(c.begin(),c.end(),target);
该写法编译器无法推导ite的类型,声明对象时未赋值,无法推导其类型
5.7.3ranged-base for
for(dec1:col1){
statement
}
编译器会找出容易col1中的每个元素,复制到dec1中,达到遍历容器的效果
for(int i : {2, 3, 4, 5, 6 })
{
cout<<i<<endl;
}
vector<double> vec;
...
for( auto elem : vec){
cout<<elem<<endl;
}//pass by value
for( auto& elem : vec){
elem *= 3;
}
//pass by reference,对vector本身进行操作,而不是副本
5.8 reference
三种变量 value pointer reference
int x = 0;
int* p = &r;
int& r = x; //r代表x,现在r,x都是0
int x2 = 5;
r = x2; //r不能重新代表其他物体,现在r,x都是5
int r2 = r;// 现在r2是5(r2代表r,亦相当于代表x)
声明一个reference时,一定要有初始值,reference一旦声明后就不能再代表其他变量
reference的常见用途
a.用于参数传递,pass by reference
b.用于返回值,return by reference
通常不用于声明变量
double imag(const double& im) {}
double imag(const double im){}
函数签名相同,无法共存
const是函数签名的一部分,加了const即算重载