第三部分:C++新特性
2.1 导读
勿在浮沙筑高台
更多细节与深入
- 泛型编程(Generic Programming)和面向对象编程(Object-Oriented Programming)虽然分属不同思维,但它们正是C++的技术主线,所以本课程也讨论模板模板(template)。
- 深入探索面向对象继承关系(Inheritance)所形成的对象模型(Object Model),包含影藏于底层的this指针,vptr虚指针,vtbl虚表,virtual mechanism虚机制以及虚函数(virtual functions)造成的多态(polymorphism)效果。
- 推荐书籍:
C++ Primer(第五版)
The C++ Programming Language(Fourth Edition)
Effective C++改善程序设计与设计思维的55个有效做法
The C++ Standard Library
STL源码剖析
2.2 conversion function, 转换函数
(1)使用场景:我自定义一个类,表示分数,但是我希望它做加减乘除的时候,自动转为double。
class Fraction { public: Fraction(int num, int den=1) : m_numerator(num),m_denominator(den) { } operator double() const //通常都会加const 不改变数据 { return (double)(m_numerator / m_denominator); } operator string() const { } private: int m_numerator; //分子 int m_denominator;//分母 }; //转换函数的应用:将Fraction型转换为double Fraction f(3,5); //构造函数 double d = 4 + f; //调用operator double()将f转为0.6
(2) non-explicit one argument constructor 非explicit单实参构造函数
class Fraction
{
public: Fraction(int num, int den=1) //non-explicit-one-argument ctor
: m_numerator(num),m_denominator(den) { }
Fraction operator+(const Fraction& f)
{
return Fraction(...);
}
private: int m_numerator; //分子
int m_denominator;//分母
};
//转换函数的应用:调用non-explicit ctor将4转换为Fraction(4,1)
// 然后调用operator+ Fraction f(3,5);
Fraction d2 = f + 4;//调用non-explicit ctor将4转为Fraction(4,1), 将数据转为Fraction
//然后调用operator+设计模式里面的【代理】,就用到了转换函数。
class Fraction
{
public: //explict只有用在一个参数的构造函数
explict Fraction(int num, int den=1) : m_numerator(num),m_denominator(den) { }
//explicit-one-argument ctor ,explict告诉编译器不要将3变为3/1
Fraction operator+(const Fraction& f)
{
return Fraction(...);
}
private: int m_numerator;
//分子 int m_denominator;
//分母
};conversion function 转换函数 模板中的应用举例
template<class Alloc>
class vector<bool,Alloc>
{
public:
typedef __bit_reference reference;
protected: reference operator[](size_type n)
{
return *(begin() + difference_type(n));
}
};
struct __bit_reference
{
unsigned int* p;
unsigned int mask;
...
public:
operator bool() const
{
return !(!(*p & mask));
} //转换函数
};2.3 pointer-like classes, 关于智能指针
(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; ... };
//应用
struct Foo
{
...
void method(void) { ... }
};
shared_ptr<Foo> sp(new Foo);
Foo f(*sp); //调用智能指针的 operator*()重载函数 sp->method();
//调用智能指针的 operator->()重载函数
//px->method();
//->得到的东西要继续->作用下去(->的特殊之处)像指针的类,用起来跟指针一样,但是有更多的机制(通过重载实现)。
链表的node也是一种pointer-like classes,真挺厉害的。

关于迭代器
template<class T>
struct __list_node{ void* prev; void* next; T data; };
template<class T,class Ref, class Ptr>
struct __list_iterator
{
typedef __list_node<T>* link_type;
link_type node; reference operator*() const
{
return (*node).data;
} //迭代器用*,就是要拿data
pointer operator->() const
{
return &(operator*());
}
};
//用法 list<Foo>::iterator ite; ... *ite;
//获得一个Foo object ite->method();
//意思是调用Foo::method()
//相当于(*ite).method();
//相当于(&(*ite))->method();2.4 function-like classes, 所谓仿函数
(1)让一个类像函数被调用,只要实现()操作符重载即可。
小小的类,用作base unit。
template <class T>
struct identity
{
const T& operator()(const T& x) const
{ return x; }
};
template <class Pair>
struct select1st
{
const typename Pair::first_type& operator()(const Pair& x) const
{ return x.first; }
};
template <class Pair>
struct select2nd
{
const typename Pair::second_type& operator()(const Pair& x) const
{ return x.second; }
};
template<class T1, class T2>
struct pair
{
T1 first;
T2 second;
pair() : first(T1()),second(T2())
{ }
pair(const T1& a, const T2& b) : first(a), second(b) {}
...
};
select1st<Pair>()(); //第一个()创建临时对象(2)标准库中的仿函数的奇特模样
template <class T>
struct identity : unary_function<T,T>
{
const T& operator()(const T& x) const { return x; }
};
template <class Pair>
struct select1st : unary_function<Pair,typename Pair::first_type>
{
const typename Pair::first_type& operator()(const Pair& x) const
{ return x.first; }
};
template <class Pair>
struct select2nd : unary_function<Pair,typename Pair::second_type>
{
const typename Pair::second_type& operator()(const Pair& x) const
{ return x.second; }
};
template <class T> struct plus : binary_function<T,T,T>
{
T operator()(const T& x, const T& y) const
{ return x+y; }
};
template <class T> struct minus : binary_function<T,T,T>
{
T opetator()(const T& x, const T& y) cosnt
{ return x-y; }
};
template <class T> struct equal_to : binary_function<T,T,bool>
{
bool operator()(const T& x, const T& y)
{ return x==y; }
};
template <class T> struct less : binary_function<T,T,bool>
{
bool operator()(const T& x, const T& y) const
{ return x<y;}
};(3)标准库中,仿函数所使用的奇特的base classes
template <class Arg, class Result>
struct unary_function
{ //大小就是把数据加起来 理论=0 实际=1
typedef Arg argument_type;
typedef Result result_type;
};
template <class Arg1, class Arg2, class Result>
struct binary_function { //大小就是把数据加起来 理论=0 实际=1
typedef Arg1 first_argument_type;
typedef Arg2 second_argument_type;
typedef Result result_type;
};
less<int>::result_type->bool2.5 namespace经验谈
using namspace std;
//--------------------------------------------
#include <iostream>
#include <memory> //shared_ptr
namespace jj01 //命名空间jj01
{
void test_member_template() { ... }
} //namespace jj01
//--------------------------------------------
#include <iostream>
#include <list>
namespace jj02 //命名空间jj02
{
template<typename T> using Lst = list<T,alloctor<T>>;
void test_template_template_param() { ... }
}//namespace jj02
//--------------------------------------------
int main(int argc, char** argv)
{
jj01::test_member_template(); //通过命名空间调用
jj02::test_template_template_param();
return 0;
}2.6 class template 类模板
template<typename T>
class complex
{
public:
complex (T r=0,T i=0) : re(r), im(i) { }
complex& operator += (const complex& );
T real() const { return re; }
T imag() const { return im; }
private: T re, im;
friend complex& __doapl(complex*, const complex&); };
int main()
{
complex<double> c1(2.5, 1.5);
complex<int> c2(2,6);
...
}2.7 Funtion Template 函数模板
template <class T>
inline const T& min(const T& a, const T& b)
{
return b < a ? b : a;
}
class stone
{
public:
stone (int w, int h, int we) : _w(w), _h(h), _weight(we)
{ }
bool operator< (const stone& rhs) const
{
return _weight < rhs._weight;
}
private: int _w, _h, weight;
};
//应用
{
stone r1(2,3), r2(3,3), r3; r3 = min(r1, r2); //编译器会对function template进行实参推导(argument deduction)
}
template <class T>
inline const T& min(const T& a, const T& b)
{
return b < a ? b : a; //实参推导的结果,T为stone,于是调用stone::operator<
}2.8 Member Template 成员模板(高级应用)
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){}
};STL中的成员模板的应用
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; //up-cast
shared_ptr<Base1> sptr(new Derived1); //模拟up-cast
2.9 specialization,模板特化 (泛化就是模板)
//一般的泛化
template <class Key>
struct hash { };
//模板特化
char int long template<> //hash<char>被绑定到char template<>为空
struct hash<char>
{
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); //hash<long>()临时对象,(1000)调用operator()函数2.10 partial specialization, 模板偏特化
1)个数的偏特化
template<typename T,typename Alloc=...> class vector { ... }; template<typename Alloc=...> class vector<bool,Alloc> //T绑定bool,减少了个数 { ... }
2)范围的偏特化
template <typename T> class C { ... }; template <typename T> class C<T*> //范围缩小为指针 { ... }; //这样写也可以 template <typename U> class C<U*> { ... }; C<string> obj1; C<string*> obj2;
2.11 template template parameter 模板模板参数(高级应用)
template<typename T, template <typename T> class Container >
//在模板template<typename ...> = template<class ...>
class XC1s
{
private:
Container<T> c;
public:
...
};
template<typename T>
using Lst = List<T,allocator<T>>;
XCls<string,Lst> mylst2; //Lst是个模板,为了可传入任何类型,外部再套一层模板template<typename T, template <typename T> class SmartPtr > class XC1s { private: SmartPtr<T> sp; public: XC1s() : sp(new T) { } };
这不是template template parameter ???不是太懂,看书好好消化
2.12 关于C++标准库(使用它熟用它)
1)Sequence containers:
array
vector
deque
forward_list
list
2) Associative containers:
set
multiset
map
multimap
3) Container adaptors:
stack
queue
priority_queue
4) Algorithms
Sorting:
sort
stable_sort
partial_sort
partial_sort_copy
is_sorted
is_sorted_until
nth_element
Binary_search:
lower_bound
upper_bound
equal_range
binary_search
lower_bound
upper_bound
equal_range
binary_search
Merge:
merge
inplace_merge
includes
set_union
set_intersection
set_difference
set_symmetric_difference
了解你的编译器对C++2.0的支持度,
C++11 complier support shootout: Visual Studio, GCC, Clang, Intel (搜索主题)
DevC++上的ISO C++开关
确认支持C++11:macro __cplusplus
#include <iostream> int main() { std::cout<<__cplusplus; }
2.13 三个主题:
(1)variadic templates(since C++11) 数量不定的模板参数
print(7.5,"hello",bitset<16>(377),42); //函数1 void print() { } //模板函数2 template<typename T, typename... Types> //模板参数包 void print(const T& firstArg, cosnt Types&... args) //函数参数类型包 { //Inside valiadic templates,sizeof...(args)yields the number of arguments cout<<firstArg<<endl; print(args...); //用递归的方式 } //函数参数包 ...就是一个所谓的pack(包)
(2)auto (since C++11)
list<string> c; ... list<string>::iterator ite; ite = find(c.begin(),c.end(),target); //since C++11 auto编译器自动推导 list<string> c; ... auto ite = find(c.begin(),c.end(),target);
(3)ranged-base for(since C++11)
for( decl : coll ){ statement } for ( int i : {2,3,4,5,7,9,13,17,19} ){ cout<< i << endl; } vector<double> vec; ... for (auto elem : vec){ //pass by value cout<< elem << endl; } for (auto& elem : vec){ //pass by reference elem *= 3; }
2.14 Reference (引用)
object 和 其reference的大小相同,地址也相同(全都是假象)
int x = 0;
int* p = &x; //p是指针变量,p is a pointer to x,p是一个变量,是int*(pointer to int)类型
int& r = x; //r 代表 x.现在r,x都是0,r变量,r is a reference to x int x2 = 5;
//注意:1、sizeof(r)==sizeof(x)
// 2、&x == &r
// 3、reference一定要定义时设初值,不可改变 r = x2;
//r引用不能重新指向别的变量,r=x=5 int& r2 = r; //r2代表r也代表x,都是5value本身、指针、引用(别名)
reference的常见用途:
reference通常不用于声明变量,而用于参数类型(parameters types)和 返回类型(return type)的描述。
void func1(Cls* pobj) { pobj->xxx(); }
void func2(Cls obj) { obj.xxx(); }
void func3(Cls& obj) { obj.xxx(); } //被调用端写法相同,很好
Cls obj;
func1(&obj); //接口不同
func2(obj);
func3(obj);
//调用接口相同,很好 reference通常不用于声明变量,
而用于参数类型(parameters type)和返回值类型(return type)的描述
一下被视为 "same signature"(所以二者不能相同)
double imag(const double& im) {...}
double imag(const double im) {...}
----------------------signature相同 Ambiguity 函数后可加const区分
Q:const 是不是函数签名的一部分?
A:是的,其中一个加const后可以并存2.15 复合&继承关系下的构造和析构
1)inheritance(继承)关系下的构造和析构
base class 的 dtor必须virtual, 否则会出现undefined behavior。
构造由内而外:
Derived的构造函数必须调用Base的default构造函数,然后才执行自己。
Derived::Derived(...) : Base() {...};
析构由外而内:
Derived的析构函数首先执行自己,然后才调用Base的析构函数。
Derived::~Derived(...){ ...~Base(); };
2) Composition(复合)关系下的构造和析构

构造由内而外:
Container的构造函数首先调用Componet的default构造函数,然后才执行自己。
Container::Container(...) : Component() {...};
析构由外而内:
Container的析构函数首先执行自己,然后才调用Component的析构函数。
Container::~Container(...) { ... Component() };
3)Inheritance + Compositon关系下的构造和析构
构造由内而外:
Derived的构造函数首先调用Base的default构造函数,然后调用Component的default构造函数,
然后才调用自己。
Derived::Derived(...) : Base(), Component() {...};
析构由外而内:
Derived的析构函数首先执行自己,然后调用Component的析构函数,
然后调用Base的析构函数。
Derived::~Derived(...){...~Component(),~Base() };
2.16 关于vptr和vtbl ,虚指针,虚表
只要类中有虚函数,就有虚指针vptr,它指向vtbl,表里面放的都是函数指针,指向内存里面的虚函数。
这幅图画得非常非常清晰。
父类有虚函数,子类也有虚函数。
动态绑定:
1)指针调用
2)向上转型
3)虚函数
这个也就是实现多态的底层原理。
2.17 关于this
Template Method 模板方法
this
//Application
framework CDocument::OnFileOpen()
{
... //常规动作
Serialize();
...
}
virtual Serialise(); //基类中关键的这部分不知道怎么做,需要继承后才知道定义为虚函数
//Application
class CMyDoc:public CDocument
{
virtual Serialize(){...}
//
};
int main()
{
CMyDoc myDoc;
...
myDoc.OnFileOpen(); //
}2.18 关于Dynamic Binding
静态绑定:直接访问内存里编译好的函数内存空间。
动态绑定:调用三个条件:
- 必须通过指针调用。(this指针调用即可满足)
- 必须是up-cast(子类向父类转型)(调用父类的函数即可满足)
- 必须调用的是虚函数 (父类需要动态绑定的函数定义为虚函数)
底层勘探
假如A是B的父类,B是C的父类。
//静态绑定 B b; A a= (A)b; //向上转型 a.vfunc1(); //调用父类的虚函数,这是静态绑定,汇编形式:call xxx //动态绑定 A* pa = new B; //向上转型 pa->vfunc1(); //调用父类的虚函数,动态绑定
2.19 谈谈const
这里C++ primer这本书说得贼全,但是很复杂。侯老师说得比较精炼。
当成员函数的const和non-const版本同时存在,
const object 智能调用const版本。
non-const object只能调用non-const版本。
const object (data member不得改动) | non-const object (data members可改动) | |
const member functions (保证不改变data members) | 允许 | 允许 |
non-const member functions (不保证 dat members不变) | 不允许 | 允许 |
即常量对象无法调用非常量成员函数。
const member functions(常量成员函数),const成员函数保证不更改data members。
COW: Copy and Write
const member function不必考虑COW(访问同一个内存空间的object)
但是non-const member function是必须考虑COW(申请一个新的内存空间,copy过去)。
当成员函数的const和non-const同时存在的时候,
C++特性:const object智能调用const版本,non-const object只能调用non-const版本。
//const是属于签名的一部分 charT operator[](size_type pos) const { ...//不必考虑COW(Copy On write) 常量字符串不可改 } reference operator[](size_type pos) { ...//必须考虑COW 非常量字符串调用 }
2.20 关于New,Delete
new: 先分配memory, 再调用ctor
String* ps = new String("Hello"); //编译器转化为 void *mem = operator new ("Hello"); //内部调用malloc(n) ps = static_cast<String*>(mem); ps->String::String("Hello"); //调用构造函数
delete: 先调用dtor,再释放memory
String* ps = new String("Hello"); ... delete ps; //编译器转化为 String::~String(ps); //析构函数 operator delete(ps); //释放内存 其内部调用free(ps)
2.21 重载 ::operator new,::operator delete
::operator new[], ::operator delete[]
这些都是全局的函数,重载的是全局的函数,小心影响无远弗界
重载member operator new/delete,做一个内存池
重载member operator new[ ]/delete[ ],做一个内存池
2.22 示例
2.23 重载new(),delete()
2.24 Basic_String使用new(extra)扩充申请量
Rep用于存储计数的
template<...>
class basic_string
{
private:
struct Rep
{
...
void release() { if(--ref==0) delete this; }
}
inline static void* operator new(size_t, size_t);
inline static void operator delete(void*);
inline static Rep* create(size_t);
...
};
template<class charT, chass traits, class Allocator> inline basic_string<charT,traits,Allocator>::Rep* basic_string<charT,traits,Allocator>::Rep:: create(size_t extra)
{
extra = frob_size(extra+1);
Rep *p = new(extra)Rep;
//
...
return p;
}
template<class charT, chass traits, class Allocator>
inline void* basic_string<charT,traits,Allocator>::Rep:: operator new(size_t s,size_t extra)
{
return Allocator::allocate(s+extra*sizeof(charT));
}
template<class charT, chass traits, class Allocator>
inline void* basic_string<charT,traits,Allocator>::Rep:: operator delete(void* ptr)
{
Allocator::deallocate(ptr,sizeof(Rep)+ reinterpret_cast<Rep *>(ptr)->res* sizeof(charT)); }