Cpp
on Programming
From C++ Primer Plus
运算符()重载与仿函数
运算符 ()
在stl queue等容器中,需要重载比较运算符()而不是 <
struct cmp {
bool operator () (triple e1,triple e2) {
return e1.dist > e2.dist;
}
};
priority_queue<triple,vector<triple>,cmp> pq;
cmp中的()运算符重载是仿函数
既能想普通函数一样传入给定数量的参数,还能存储或者处理更多我们需要的有用信息
class ShorterThan {
public:
explicit ShorterThan(int maxLength) : length(maxLength) {}
bool operator() (const string& str) const {
return str.length() < length;
}
private:
const int length;
};
count_if(myVector.begin(), myVector.end(), ShorterThan(length))
//count_if规定了第三个参数只能接受一个参数,但要使得方便修改,利用仿函数即可
}
hash_function
static size_t hash(const pair<int,int> & a) {
return a.first * 3005 + a.second;
}
unordered_map<edge,int,size_t(*)(const pair<int,int> &)>es(n,hash);
unordered_map(size_type __n,
const hasher& __hf = hasher(),
const key_equal& __eql = key_equal(),
const allocator_type& __a = allocator_type())
: _M_h(__n, __hf, __eql, __a)
{ }
在上述中使用哈希表需要传递hash function,从声明中知有size和hasher,key_equal()使用==操作符,pair已经有重载
除了上述的直接构建实例(注意static,成员函数指针和静态函数指针不同),还可以使用前面提到的仿函数
C++函数
左值与右值
左值,有名称的量,可以通过变量名直接访问
右值,没有名称,存在于内存中的量
a = 1;//左值
int && a = fun();//fun()的返回值是右值类似的还有常量
引用
可以多层函数嵌套对对象进行修改,但是需要注意避免返回函数终止时不再存在的内存单元引用
同时可以进行赋值
type & function(type & arg)
function(arg) = newarg
当返回是引用时则可以这样,否则不行。或者返回使用const
const type & function(type & arg)
function(arg) = newarg (此时则不允许这样)
什么时候在函数调用时创建临时变量:
当实参与引用参数不匹配,C++将生成临时变量。目前当参数为const时:(1)实参的类型正确,但不是左值(可被引用的数据对象)(2)类型不正确,但可以转换为正确的类型
默认参数
int function(int tk = 1,int m = 2,int n = 3)
有默认参数的右侧的参数也必须都有默认参数
函数重载
函数重载的关键是特征标——函数的参数列表,而不是函数的返回类型。因此函数重载返回类型可以不同。
模板
模板同样可以重载
将模板用作参数
template <template <typename T>class Thing>
class A{};
template <typename T>class是参数,声明Thing必须是一个模板类
模板别名
template<typename T>
using arrtype = std::array<T,12>;
显式具体化
函数有多个原型,则编译器在选择原型时,优先选择非模板版本,显式具体化优先于模板
void swap(job &, job &);
template <> void swap<job>(job &,job &);//显式具体化
template <typename T>
void swap(T &,T &);
显式具体化与显式实例化不同
template void swap<int>(int,int); //显式实例化
//使用模板生成一个swap的实例
template <> void swap<int> (int ,int ) //显式具体化
template <> void swap (int ,int ) //显式具体化
template <>表示不要使用swap模板来生成一个使用int类型的实例
模板中的符号
使用decltype(arg),用于模板中推断临时变量的类型
后置返回类型
template<typename T1,typename T2>
auto func(T1 x,T2 y) -> decltype(x + y) {
return x + y;
}
类
构造函数
explicit 声明的构造函数不允许默认隐式转换和复制转换
class A {
A(int) {}
A(int,int) {}
};
A a = 1;\\在没有explicit下允许
A a = {1,2};\\在没有explicit下允许
A b = A(a);\\拷贝构造函数,未重载时默认提供
只有接受一个参数的构造函数才能作为转换构造函数
转换函数
operator typename();
友元函数
非成员函数无法访问类的私有成员
类的友元函数是非成员函数,其访问权限与成员函数相同
friend func(args);\\声明在类中
友元函数不能继承
友元函数用于类继承时,因为友元不是成员函数,所以不能使用作用域解析运算符来指出要使用哪个函数,所以使用强制类型转换,使得匹配原型时能够选择正确的函数
Private,friend,public,protected
friend的作用见上
protected用于类继承时,派生类对声明为protected的基类的成员可以直接访问
虚函数
基类中对应的函数需要声明为virtual,派生类中对应的函数也许要声明
如何工作:维护一个虚函数表,查看派生类和基类的虚函数,如果在派生类中有对应的虚函数,则将派生类的虚函数填入表中,如果没有则将基类的虚函数填入表中
用虚函数实现多态,因为可以用基类的指针和引用指向派生类,这时候调用对应的函数,如果是虚函数中,则会根据对象实际的类型(编译器跟踪)选择实际调用的函数
编译:动态联编
析构函数通常是被成名为虚函数
类继承
需要注意类复制构造函数,赋值运算符,特别是成员中含有指针,而指针需要使用new来分配空间,需要深拷贝
使用复制构造函数的情况:
- 将新对象初始化为一个同类对象
- 按值将对象传递给函数
- 函数按值返回对象
- 编译器生成临时对象
使用赋值运算符的情况:
- 如果语句创建新的对象,则使用初始化;如果语句修改已有对象的值,则是赋值。
私有继承
私有继承是另一种实现has-a关系的途径,所以成员都将成为派生类的私有成员
在私有继承中如何访问基类的对象?
答:类型强制转换
同样的对于基类的友元函数也可以通过类型强制转换的方式进行。
实现has-a的方式有两种:私有继承和包含
通常使用包含,但是私有继承可以访问protected成员
保护成员
基类的公有成员和保护成员都将成员派生类的保护成员,基类的接口在三代继承中仍然可用,而私有继承不可用
特征 | 公有继承 | 保护继承 | 私有继承 |
---|---|---|---|
公有成员变成 | 派生类的公有成员 | 派生类的保护成员 | 派生类的私有成员 |
保护成员变成 | 派生类的保护成员 | 派生类的保护成员 | 派生类的私有成员 |
私有成员变成 | 只能通过基类接口访问 | 只能通过基类接口访问 | 只能通过基类接口访问 |
能否隐式向上转换 | 是 | 是(只能在派生类中) | 否 |
多重继承(MI)
class Singer:public Worker{};
class Waiter:public Worker{};
class SingerWaiter: public Worker,public Worker{};
这样SingerWaiter中有两个worker对象,不能进行隐式类型转换(派生类向基类转换)
要使得只继承一个,使用虚基类
class Singer:virtual public Worker{};
class Waiter:public virtual Worker{};
class SingerWaiter: public Worker,public Worker{};
虚基类的构造函数不会传递,需要显式指明work的构造函数
当祖先相同时,必须要引入虚基类
当继承了多个同名的方法,需要使用类限定符
STL容器
class A{
typed
};
X::value_type
vector
vector的操作中只有push_back()是O(1)时间复杂度,从头插入,删除都是O(n)复杂度
模板initializer_list
可以用于可变参数
初始化
vector<int> a = {1,2,3}//使用了initializer_list
RTTI
运行阶段类型识别
dynamic_cast<pointer>:将使用一个基类的指针来生成一个指向派生类的指针
typeid(运算符):返回一个指出对象的类型的指
type_info(类):存储了有关特定类型的信息
typeid(Class A) == typeid(pointer)
typeid接受类名或结果为对象的表达式,返回一个对type_info对象的引用,type_info中可以比较两个类型
智能指针
auto_ptr<> //不能两个之间赋值,会导致delete两次
shared_ptr<>//引用计数
unique_ptr<>//有所有权概念
C++11标准
移动语义和右值引用
新的类功能
默认的方法和禁用的方法
default和delete
委托构造函数,继承构造函数
构造函数可以调用基类的构造函数和其他的构造函数
管理虚方法
override:重写覆盖对应的虚方法
final:不允许重写该虚方法
Lambda函数,函数体,包装器
[&,=a]() {} \\lambda表达式,和函数指针、函数体、函数统称为called type
std::function<ret type(args type)> name;\\可将以上的called type使用function包装,可以直接调用function的构造函数
std::function<int(int)> var([=count](int a) {return a + count;})
可变参数模板
template<typename T> void show_list(const T& value) { std::cout << value << '\n'; }
template<typename T, typename...Args> void show_list(const T&value, const Args& ... args) {
std::cout << value << ",. ";
show_list(args...);
}
//使用函数重载和解包递归的操作