革命尚未成功,同志仍需努力💪
1、导读
1) 泛型编程(Generic Programming)和面向对象编程(Object-Oriented Programming)是c++的技术主线。
推荐书记:《c++ primer》第五版;《c++ programming language》《effective c++》(有中文版)《effective modern c++》《the c++ standard library》《STL源码剖析》
模版库是用模版编程的思维做出来的。
2、Conversion Function(转换函数)
class A创建的对象转换成别的类型,或者将别的类型转为class A类型
class Fraction
{
public:
Fraction(int num, int den = 1)
:m_numerator(num), m_denominator(den) { }
operator double() const {
//operator double() 想将这个类转换成double类型,double里面不可有参数,没有返回类型,转换不应有改变,通常加const
return (double)(m_numerator / m_denominator);
}
private:
int m_numerator;
int m_denominator;
};
Fraction f(3,5);
double d = 4+f; //调用operator double()将f转为0.6
3、non-explicit-one-argument ctor 不是explicit类型 一个参数有默认值,只需要给一个参数赋值即可(可以把别的东西转换成这种)
1)
class Fraction
{
public:
Fraction(int num, int den = 1)
: m_numerator(num), m_denominator(den) { }
Fraction operator+(const Fraction& f)
return Fraction(......);
private:
int m_numerator;
inr m_denominator;
}
Fraction f(3,5);
Fraction d2 = f+4; //调用non-explicit ctor 将4转为Fraction(4,1),然后调用operator+
2)explicit-one-argument ctor
(explicit明白的,明确的)告诉编译器不要随便给我做事情,用到构造函数再调用,不可以把3变成3/1
class Fraction
{
explict Fraction(int num, int den=1)
:m_numerator(num), m_denominator(den) { }
operator double() const{
return (double) (m_numerator / m_denominator);}
Fraction operator+(const Fraction& f) { //这里定义的加法,左右两边都应该是Fraction
return Fraction(......);
}
private:
int m_numerator;
int m_denominator;
};
Fraction f(3,5);
Fraction d2=f+4; //[Error] 4无法变成Fraction类型
explict大多数情况下都用在构造函数前面。
template<class Alloc> // templete<typename name>
class vector<bool, Alloc> //模版的偏特化,vector中存放的每一个值是bool类型
{
public:
typedef __bit_reference reference;
protected:
reference operator[ ] (size_type n) //对[ ]做重载,传回值是reference,应该传回bool值,但是现在传回的是reference,需要一个转换函数将其转换为bool值
{return *(begin() + difference_type(n));}
...
}
struct __bit_reference
{
unsigned int* p;
unsigned int mask;
public:
operator bool() const { return !(!(*p & mask));} //转换函数,将reference值转换成bool值
...
4)pointer-like classes
<1>关于智能指针(比指针功能更多)
class对象像指针或者像函数
智能指针里面一定带有一个真正的指针,有* 和 -> 两种操作
templete<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;
}
<2> 关于迭代器
代表容器中一个元素,遍历容器
5、function-like classes,仿函数
如果class里面有 operator( ) 则是仿函数
6、namespace //不同团队开发代码,把自己团队的都放到一个namespace下,防止和别人重名
namespace jj01
{ }
namespace jj02
{ }
测试:
int main(int argc, char** argv)
{
jj01::test_member_template();
jj02::test_template_template_param();
}
c++模板
模板是实现代码重用机制的一种工具,可以实现类型参数化,把类型定义为参数,从而实现代码可重用
模板分类:函数模版和类模版,成员模版。函数模版针对参数类型不同的函数;类模版仅针对数据成员和成员函数类型不同的类
注意:模版的声明或定义只能在全局,命名空间或类范围内进行。不能在局部范围,函数内进行,如不可在main()函数中声明或定义一个模版。
7、class template,类模板
设计一个类其某些变量可以被用户任意改变
8、Function Template,函数模板
函数模板使用时不用写类型
//学过c的童鞋们一定都写过函数sum吧,当时是这样写的:
int sum(int a,int b){ return a+b;}//实现了整数的相加
//如果再想同时实现小数的相加,就再多写个小数的相加。普通实现我就不写了,知道函数重载的童鞋们会这样写:
int sum(int a, int b){
//第一个function
return a+b;}
double sum(double a,double b){
//第二个function
return a+b;}
//这样我们就可以只用一个sum函数 就可以实现整数相加与小数相加。
//但是这样我们还是要定义两个函数。
//C++考虑了怎么避免这种重复的操作,代码如下: 函数模板的声明。
templete <typename T>
T sum(T a, T b)
{return a+b;}
//只需要定义一个函数与只使用一个函数实现两数相加。
9、member template,成员模板
语法:
template <class T1, class T2>
template <class u1, class u2>
template<typename _Tp>
class shared_ptr:public __shared_ptr<_Tp>
{
...
template<typename _Tp1>
explicit shared_ptr(_Tp1* __p)
:__shared_ptr<_Tp>(__p) { }
...
};
Base1* ptr = new Derived1;
shared_ptr<Base1>sptr(new Derived1);
10、模版特化(某些独特的类型要做特殊设计,将模版局部的特征化)
模版泛化,有一个类型,用时指定
template <class Key>
struct hash { }; //泛化
template<> // 以下为特化。Key被绑定,所以不在这里显示
struct hash<char>
{
//如果用户指定类型是char,int,long,可以用以下代码
size_t operator () (char x) const {return x;} //对()做重载
};
template<>
struct hash<int>
{
size_t operator() (int x) const { return x; }
};
template<>
struct hash<long>
{
size_t operator() (long x) const { return x; }
};
使用 cout << hash<long> () (1000); 会去找有对应的特化,则用特化,1000的()是重载,1000是long x。
11、模版偏特化(局部特化)
1)个数的偏
模版中有多个模版参数,此例中有两个,要绑定其中一个(绑定要依次从左到右)。
2)范围的偏
任意类型 变成指针指向的类型
templete <typename T>
class C
{ ... }; //先写一个任意类型的
templete <typename U>
class C<U*> //当用户是指针类型的要用这个(偏特化)
{ ... };
根据用户不同的调用形式是否有指针,来调用不同段的代码
12、模版模版参数
template<typename T, template <typename T> class Container> //模版模版参数
class XCls
{
private:
Container<T> c;
public:
......
};
13、c++标准库
标准库提供给我们:数据结构,容器,算法
标准库给出的要直接用 不需要自己写
编译器 Dev c++ 支持c++11。project 、Project Option
14、三个主题(c++11)
1)数量不定的模版参数
void print() //最后剩零个参数调用此函数
{
}
template<typename T, typename...Types> //参数很多个,把参数分为一个和一个包(...)
void print(const T& firdtArg, const Type&...args)
{
cout << firstArg << endl;
print(args...); //先打印7.5,然后再调用print将剩下的参数分为一个和一包,再进行递归。
}
调用:print(7.5, "hello", bitset<16>(377),42);
%%一个和一包(pack)里面所做的操作任意
求一包里面参数个数:sizeof...(args) //要加...
2)auto(since c++11)
以前版本:
list<string> c;
...
list<string>::iterator ite;
ite = find(c.begin(),c.end(),target);
c++11:
list<string> c;
...
auto ite = find(c.begin(), c.end(), target); 编译器通过等号右边自动推出左边auto是什么
//下面写法是错误的
list<string> c;
...
auto ite; //这里❌ 编译器无法推测auto类型
ite = find(c.begin(), c.end(), target);
3)ranged-base for
for(decl : coll) //decl是变量,coll是容器,将容器中值赋给左边变量,再进行下面计算
{ statement }
for(int i: {2,3,5,7,9,13,17,19}) //在c++11中容器可以直接用大括号列出来
{cout << i << endl;}
for(auto& elem: vec) {elem *= 3;} 传引用,改变原来值。//引用就是指针