C++程序设计
# 转换函数 (conversion function)
格式: operator type() const
说明: 要以operator开头,函数名称为需要转成的类型,不可以有参数。前面不需要写返回类型,因为c++会自动返回函数名称这个类型。
转换函数通常后面有const,即不需要改变数据则要加const。
class Fraction
{
public:
Fraction(int num,int den=1) : m_numerator(num),m_denominator(den){ }
operator 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
# non-explicit-one-argument ctor
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;//分子
int m_denominator;//分母
};
Fraction f(3,5);
Fraction d = f+4;//调用non-explicit ctor 将4转为Fraction(4,1),然后调用operator+
- 这里没有写转换函数,而是重载了+操作符。
- 重载之后的+是分数+分数,编译器处理d2 = f+4的时候,发现右边不是分数,则看4能否转换成分数。
- 因为是只需要一个实参的构造函数,因此4可以转为分数,则可以调用重载之后的+。
- 因此non-explicit-one-argument ctor可以把其他类型转换为该类型。
# conversion function VS. non-explicit-one-argument ctor
class Fraction
{
public:
Fraction(int num,int den=1) : m_numerator(num),m_denominator(den){ }
Fraction operator+(const Fraction& f){
return Fraction(...);
}
operator double() const{
return (double)(m_numerator/m_denominator);
}
private:
int m_numerator;//分子
int m_denominator;//分母
};
Fraction f(3,5);
Fraction d = 4+f;//[Error] ambiguous
如果这两个并存了,编译器就不知道该调用哪个了。(不知道把分数转为double还是把int转为分数)
# explicit-one-argument ctor
class Fraction
{
public:
explicit Fraction(int num,int den=1) : m_numerator(num),m_denominator(den){ }
Fraction operator+(const Fraction& f){
return Fraction(...);
}
operator double() const{
return (double)(m_numerator/m_denominator);
}
private:
int m_numerator;//分子
int m_denominator;//分母
};
Fraction f(3,5);
Fraction d = 4+f;//[Error] conversion from double to Fraction requested
- 构造函数加上explicit之后,表示这个构造函数只能在构造的时候使用,不会在转换类型时使用了。
- 这个explicit关键字主要就出现在这里。
# 模板
# 类模板
类模板用于实现类所需数据的类型参数化。 类模板在使用时,必须指定类型。
# 类模板派生普通类
子模板类派生时,需要具体化模板类,c++编译器要知道父类的数据类型具体是什么样的,因为 c++编译器要分配内存,必须知道父类所占内存大小
class SubClass : public MyClass<int>{};
# 类模板派生类模板
template
应该把类模板的声明和实现写在一起
# 类模板中的static关键字
- 从类模板实例化的每一个模板类有自己的类模板数据成员,该模板的所有对象共享一个 static 数据成员
- 和非模板类的 static 数据成员一样,模板类的 static 数据成员也应该在文件范围 定义和初始化
- 每个模板类有自己类模板的 static 数据成员的副本
# 函数模板
- 函数模板调用,将会严格匹配类型,不会进行自动类型转换;
- 普通函数调用,可以进行隐式类型转换。
- 编译器会对函数模板进行两次编译,在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译。
# 成员模板
# 例1
template<typename T1,typename T2>
struct pair{
...
template<typename U1,typename U2>
pair(const pair<U1,U2>& p):first(p.first),second(p.second){}
...
};
pair<Base1,Base2> p(pair<Derived1,Derived2>());
# 例2
template<typeneme _Tp>
class shared_ptr:public __shared_ptr<_Tp>{
...
template<typename _Tp1>
explict shared_ptr(_Tp1* __p):__shared_ptr<_Tp>(__p){}
...
};
Base1* ptr = new Derived1; //up-cast
shared_ptr<Base1> sptr(new Derived1); //模拟up-cast
# 模板特化
template <class Key>
struct hash{};
template<>
struct hash<char>{};
template<>
struct hash<int>{};
template<>
struct hash<long>{};
# 模板偏特化
# 个数的偏
template <typename T, typename Alloc = ...>
class vector{};
template <typename Alloc = ...>
class vector<bool,Alloc>{};
# 范围的偏
template <typename T>
class C{};
template <typename T>
class C<T*>{};
# 模板模板参数
template<typename T,
template<typename T>
class Container
>
class XCls{
private:
Container<T> c;
public:
...
};
template<typename T>
using Lst = list<T,alloctor<T>>;
Xls<string,Lst> mylst1;
template<typename T,
template<typename T>
class SmartPtr
>
class XCls{
private:
SmartPtr<T> c;
public:
XCls():sp(new T){}
};
Xls<string,shared_ptr> p1;
Xls<double,unique_ptr> p1; //错
Xls<int,weak_ptr> p1; //错
Xls<long,auto_ptr> p1;
# 数量不定的参数模板(varidic template)
void print(){
...
}
template<typename T,typename... Types>
void print(const T& firstArg,const Types&... args){
cout << firstArg << endl;
print(args...);
}
# 仿指针类 (pointer-like classes)
# 智能指针
template<typename T>
class shared_ptr{
public:
T& operator*() const{
return *px;
}
T* operator->() const{
return px;
}
private:
T* px;
long* pn;
}
//指向操作符具有传递作用
shared_ptr<Foo> sp(new Foo);
Foo f(*sp);
sp->method();
- 设计的class,像指针。智能指针,完成比指针更多的工作。一般都是包着一层普通指针。
- 指针允许的动作,这个类也需要允许操作。
- *操作符和->操作符都需要重载。
- 这样调用sp->的时候,实际上内部重载操作符,将内部的普通指针px返回出来,然后px可以继续使用->来完成。相当于这个->符号用了两次。 详讲:https://zhuanlan.zhihu.com/p/436290273
实践:https://blog.csdn.net/albertsh/article/details/82286999
# 迭代器
stl中的迭代器也是一种指针,迭代器这种智能指针不仅需要重载* ->操作符,还需要处理++,--等符号。
# 仿函数类 (function-like classes)
设计一个class,行为像一个函数,即仿函数。
template<class T>
struct identity{
const T& operator()(const T& x) const{
return x;
}
}
- 即可以使用小括号来调用。
- 对小括号()操作符进行重载。
//一元函数对象 应用举例:for_each
class print{
public:
void operator()(const int& v){
cout << v << " ";
}
};
for_each(v.begin(),v.end(), print());
//二元函数对象 应用举例:transform
class myplus{
public:
int operator()(int v1,int v2){
return v1 + v2;
}
};
transform(v1.begin(), v1.end(), v2.begin(),v3.begin(), myplus());
//一元谓词 应用举例:find_if
class mygreater{
public:
bool operator()(const int& v){
return v > 2;
}
};
vector<int>::iterator it = find_if(v.begin(), v.end(), mygreater()); //匿名函数对象
cout << *it << endl;
//二元谓词 应用举例:sort
class mycompare{
public:
bool operator()(int v1,int v2){
return v1 > v2;
}
};
sort(v.begin(), v.end(), mycompare());
# 内建函数对象
使用内建函数对象,需要引入头文件 #include <functional>
6个算数类函数对象,除了 negate 是一元运算,其他都是二元运算。
template<class T> T plus<T>//加法仿函数
template<class T> T minute<T>//减法仿函数
template<class T> T multiplies<T>//乘法仿函数
template<class T> T divides<T>//除法仿函数
template<class T> T modulus<T>//取模仿函数
template<class T> T negate<T>//取反仿函数
6个关系运算类函数对象,每一种都是二元运算。
template<class T> bool equal_to<T>//等于
template<class T> bool not_equal_to<T>//不等于
template<class T> bool greater<T>//大于
template<class T> bool greater_equal<T>//大于等于
template<class T> bool less<T>//小于
template<class T> bool less_equal<T>//小于等于
3个逻辑运算类运算函数,not 为一元运算,其余为二元运算。
template<class T> bool logical_and<T>//逻辑与
template<class T> bool logical_or<T>//逻辑或
template<class T> bool logical_not<T>//逻辑非
//使用内建函数对象声明一个对象
plus<int> myPlus;
cout << myPlus(5, 3) << endl;
//使用匿名临时对象
cout << plus<int>()(5, 6) << endl;
# 函数对象适配器
函数对象适配器是完成一些配接工作,这些配接包括绑定(bind),否定(negate),以及对一般函数或成员函数的修饰,使其成为函数对象
bind1st :将参数绑定为函数对象的第一个参数
bind2nd : 将参数绑定为函数对象的第二个参数
not1 : 对一元函数对象取反
not2 : 对二元函数对象取反
ptr_fun : 将普通函数修饰成函数对象
mem_fun : 修饰成员函数
mem_fun_ref : 修饰成员函数
如果希望函数对象适配器能对我们自己编写的函数对象有效,我们需要根据我们的函数对象类型继承 STL 的父类对象。
二元函数继承:public binary_function<参数类型,参数类型,返回类型> 一元函数继承:public unary_function<参数类型,返回类型>
//函数适配器 bind1st bind2nd
//现在我有这个需求 在遍历容器的时候,我希望将容器中的值全部加上 100 之后显示出来,怎么做
哇?
struct myprint : public binary_function<int,int,void>{ //二元函数对象 所以需要继承binary_fucntion<参数类型,参数类型,返回值类型>
void operator()(int v1 ,int v2) const{
cout << v1 + v2 << " "; }
};
//我们直接给函数对象绑定参数 编译阶段就会报错
//for_each(v.begin(), v.end(), bind2nd(myprint(),100));
//如果我们想使用绑定适配器,需要我们自己的函数对象继承 binary_function 或者unary_function
//根据我们函数对象是一元函数对象 还是二元函数对象
for_each(v.begin(), v.end(), bind2nd(myprint(), 100));
//总结: bind1st 和 bind2nd 区别?
//bind1st : 将参数绑定为函数对象的第一个参数
//bind2nd : 将参数绑定为函数对象的第二个参数
//bind1st bind2nd 将二元函数对象转为一元函数对象
vector<int>::iterator it = find_if(v.begin(), v.end(),
not1(bind2nd(less_equal<int>(), 2)));
cout << "it:" << *it << endl;
sort(v.begin(),v.end(),not2(greater<int>()));
for_each(v.begin(), v.end(), myprint02());
cout << endl;
//not1 对一元函数对象取反
//not2 对二元函数对象取反
//如何给一个普通函数使用绑定适配器(bind1st bind2nd)绑定一个参数?(拓展)
//ptr_fun
void myprint04(int v1,int v2){
cout << v1 + v2 << " "; }
//1 将普通函数适配成函数对象
//2 然后通过绑定器绑定参数
for_each(v.begin(), v.end(), bind2nd(ptr_fun(myprint04),100));
cout << endl;
//总结: ptr_fun 将普通函数转变为函数对象
//mem_fun mem_fun_ref
//如果我们容器中存储的是对象或者对象指针,如果能指定某个成员函数处理成员数据。
class student{
public:
student(string name, int age) :name(name), age(age){}
void print(){
cout << "name:" << name << " age:" << age << endl;;
}
int age;
string name;
};
void test05(){
//mem_fun : 如果存储的是对象指针,需要使用 mem_fun
vector<student*> v;
student* s1 = new student("zhaosi",10);
student* s2 = new student("liuneng", 20);
student* s3 = new student("shenyang", 30);
student* s4 = new student("xiaobao", 40);
v.push_back(s1);
v.push_back(s2);
v.push_back(s3);
v.push_back(s4);
for_each(v.begin(), v.end(), mem_fun(&student::print));
cout << "-----------------------------" << endl;
//mem_fun_ref : 如果存储的是对象,需要使用 mem_fun_ref
vector<student> v2;
v2.push_back(student("zhaosi",50));
v2.push_back(student("liuneng", 60));
v2.push_back(student("shenyang", 70));
v2.push_back(student("xiaobao", 80));
for_each(v2.begin(), v2.end(), mem_fun_ref(&student::print));
← 算法 C++ Primer总结 →