- 是上一篇博文“面向对象程序设计”的续集
- 本文将探讨上文未讨论的主题
- 在先前培养正规、大器的编程素养上,继续探讨更多技术。
- 泛型编程(Generic Programming)和面向对象编程(Object-Oriented Programming)虽然分属不同思维,但它们正是C++的技术主线。本文也讨论template(模板)。
- 深入探索面向对象之继承关系(inheritance)所形成的对象模型(Object Model),包括隐藏于底层的this指针,vptr指针(虚指针),vtbl(虚表),virtual mechanism(虚机制),以及虚函数(virtual functions)造成的polymorphism(多态)效果。
Test-Cpp.cpp
C++编译器- 编译(compile)
- 连接(link)
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.6non-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 d2 = f + 4;//调用non-explicit ctor将4转为Fraction(4, 1),然后调用operator+conversion function vs. non-explicit-one-argument ctor
class Fraction
{
public:
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)
{
return Fraction(......);
}
private:
int m_numerator;
int m_denominator;
};
使用:
Fraction f(3, 5); Fraction d2 = f + 4;//[ERROR]ambiguous 二义explicit-one-argument ctor
class Fraction
{
public:
explicit 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)
{
return Fraction(......);
}
private:
int m_numerator;
int m_denominator;
};
使用:
Fraction f(3, 5); Fraction d2 = f + 4;//[ERROR]conersion from 'double' to 'Fraction' requested
conversion function, 转换函数
proxy
templateclass vector { 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)); }
...
pointer-like classes, 关于智能指针
templateclass 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_ptrsp(new Foo); Foo f(*sp); sp->method();
相当于
px->method();pointer-like classes, 关于迭代器
templatestruct __list_iterator { typedef __list_iterator self; typedef Ptr pointer; typedef Ref reference; typedef __list_node * link_type; link_type node; bool operator==(const self& x) const {return node == x.node; } bool operator!=(const self& x) const { return node != x.node; } reference operator*() const { return {*node}.data; } pointer operator->() const { return &(operator*());} self& operator++() { node = (link_type)((*node).next); return *this;} self operator++(int) { self tmp = *this; ++*this; return tmp;} self& operator--() { node = (link_type)((*node).prev); return *this;} self operator--(int) { self tmp = *this; --*this; return tmp; } };
使用:
listfunciton-like classes, 所谓仿函数::iterator ite; ... *ite;//获得一个Foo object ite->method(); //意思是调用Foo::method() //相当于(*ite).method(); //相当于(&(*ite))->method();
templatestruct identity { const T& operator() (const T& x) const { return x; } }; template struct select1st { const typename Pair::first_type& operator() (const Pair& x) const { return x.first; } }; template struct select2nd { const typename Pair::second_type& operator() (const Pair& x) const { return x.second; } };
template标准库中仿仿函数的奇特模样struct pair { T1 first; T2 second; pair() : first(T1()), second(T2()) {} pair(const T1& a, const T2& b): first(a), second(b) {} ...... };
templatestruct identity : public unary_function { const T& operator() (const T& x) const { return x; } }; template struct select1st : public unary_function { const typename Pair::first_type& operator() (const Pair& x) const { return x.first; } }; template struct select2nd : public unary_function { const typename Pair::second_type& operator() (const Pair& x) const { return x.second; } };
template标准库中,仿函数所使用的奇特的base classesstruct plus : public binary_function { T operator()(const T& x, const T& y) const { return x + y; } }; template struct minus : public binary_function { T operator()(const T& x, const T& y) const { return x - y; } }; template struct equal_to : public binary_function { T operator()(const T& x, const T& y) const { return x == y; } }; template struct plus : public binary_function { T operator()(const T& x, const T& y) const { return x < y; } };
templatestruct unary_function { typedef Arg argument_type; typedef Result result_type; }; template struct binary_function { typedef Arg1 first_argument_type; typedef Arg2 second_argument_type; typedef Result result_type; };
less::result_type->bool
namespace经验谈using namespace std; //----------------------------------- #include#include //share_ptr namespace jj01 { void test_member_template() { ...... } }//namespace //----------------------------------- #include #include namespace jj02 { template
using Lst = list >; void test_template_template_param() { ...... } }//namespace //-----------------------------------
使用:
int main(int argc, char** argv) jj01::test_member_template(); jj02::test_template_template();class template, 类模板
templateclass 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&); };
使用:
{
complex c1(2.5, 1.5);
complex c2(2, 6);
...
}
function template, 函数模板
stone r1(2, 3), r2(3, 3), r3; r3 = min(r1, r2);
编译器会对function template进行实参推导(argument deduction)
templateinline const T& min(const T& a, const T& b) { return b < a ? b : a; }
实参推导的结果,T为stone,于是调用stone::operator<
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;
};
member template, 成员函数
templatestruct 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 pair(const pair & p) : first(p.first), second(p.second) {} };
class base1{};
class Derived1:public base1{};
class base2{};
class Derived2:public base2{};
pairp; pair p2(p);
pairp2(pair ());
templateclass shared_ptr:public __shared_ptr<_Tp> { ... template explicit shared_ptr(_Tpl* __p) :__shared_ptr<_Tp>(__p){} ... };
base1* ptr = new Derived1;//up-cast shared_ptrspecialization, 模板特化sptr(new Derived1);//模拟up-cast
【注】特化反义词:泛化
泛化
templatestruct hash{ };
特化
template<> struct hash{ size_t operator() (char x) const { return x; } }; template<> struct hash { size_t operator() (int x) const { return x; } }; template<> struct hash { size_t operator() (long x) const { return x; } };
使用:
cout << hash() (1000);
泛化又叫full specialization,全泛化,对应偏特化。
patial specialization, 模板偏特化——个数的偏templateclass vector { ... };
绑定
templatepatial specialization, 模板偏特化——范围的偏class vector { ...
templateclass C { ... };
【注】上下的T不是一个T
templateclass C { ... };
这样写也可以
templateclass C { ... };
使用:
Ctemplate template parameter, 模板模板参数obj1; C obj2;
templateclass Container > class XCls { private: Container c; public: ...... };
templateusing Lst = list >;
XClsmylst1;//错误 XCls mylst2;
templateclass SmartPtr > class XCls { private: SmartPtr sp; public: XCls():sp(new T) { } };
XCls这不是template template parameterp1; XCls p2;//错误 XCls p3;//错误 XCls p4;
template> class stack { friend bool operator== <> (const stack&, const stack&); friend bool operator< <> (const stack&, const stack&); protected: Sequence c;//底层容器 ...... };
使用
stack关于C++标准库 容器 Sequence containerss1; stack > s2;
array
vector
deque
forward_list
list
stack
queue
priority_queue
set
multiset
map
multimap
unordered_set
unordered_multiset
unordered_map
unordered_multimap
…
Sortingsort
stable_sort
partial_sort
partial_sort_copy
is_sorted
is_sorted_until
nth_element
lower_bound
upper_bound
equal_range
binary_search
merge
inplace_merge
includes
set_union
set_intersection
set_difference
set_symmetric_difference
…
推书:Algorithms + Data Structures = Programs(Niklaus Wirth)
确认支持C++11: macro __cplusplus
测试:
VS2012
#include"stdafx.h" #includeusing namespace std; int main() { cout<<__cplusplus< Dev-C++ 5
#includeint main() { std::cout<<__cplusplus; } 如果是199711,则不支持C++11,需修改编译器
variadic templates(since C++11) 数量不定的模板参数
如果是201103,则支持C++11void print() { } templatevoid print(const T& firstArg, const Type&... args) { cout< Inside variadic templates, sizeof…(arg) yields the number of arguments
…就是一个所谓的pack(包)
用于template parameters, 就是template parameters pack(模板参数包)
用于function parameter types, 就是function parameter types pack(函数参数类型包)
用于function parameters, 就是function parameters pack(函数参数包)使用:
print(7.5, "hello", bitset<16>(377), 42);结果:
7.5 hello 0000000101111001 42auto(since C++11)过去:
listc; ... list ::iterator ite; ite = find(c.begin(), c.end(), target); 现在:
listc; ... auto ite = find(c.begin(), c.end(), target); 错误:
listranged-base for(since C++11)c; ... auto ite;//错误 ite = find(c.begin(), c.end(), target); for(decl : coll) { statement }for(int i : {2, 3, 5, 7, 9, 13, 17, 19}) { cout<< i << endl; }vectorreferencevec; ... for(auto elem : vec)//pass by value { cout << elem << endl; } for(auto& elem : vec)// pass by reference { elem *= 3; } int x=0; int* p = &x; 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)从内存上看,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bWdLqDZF-1634388189033)(https://github.com/hubojing/BlogImages/blob/master/C++程序设计之兼谈对象模型——reference.png?raw=true)]注意:
- sizeof® == sizeof(x)
- &x = &r;
object和其reference的大小相同,地址也相同(全都是假象)
Java里头所有变量都是referencetypedef struct Stag{int a, b, c, d;} S; int main() { double x = 0; double* p = &x;//p指向x,p的值是x的地址 double& r = x;//r代表x,现在r,x都是0 cout << sizeof(x) << endl;//8 cout << sizeof(p) << endl;//4 cout << sizeof(r) << endl;//8 cout << p << endl;//0065FDFC cout << *p << endl;//0 cout << x << endl;//0 cout << r << endl;//0 cout << &x << endl;//0065FDFC cout << &r << endl;//0065FDFC S s; S& rs = s; cout << sizeof(s) << endl;//16 cout << sizeof(rs) << endl;//16 cout << &s << endl;//0065FDE8 cout << &rs << endl;//0065FDE8 }object和其reference的大小相同,地址也相同(全都是假象)
reference的常见用途void func1(Cls* pobj) {pobj->xxx();} void func2(Cls obj) {obj.xxx();}被调用端 写法相同,很好 void func3(Cls& obj) {obj.xxx();}//被调用端 写法相同,很好 ...... Cls obj; func1(&obj);//接口不同,困扰 fun2(obj);//调用端接口相同,很好 func3(obj);//调用端接口相同,很好reference通常不用于声明变量,而用于参数类型(parameters type)和返回类型(return type)的描述。
以下被视为"same signature"(所以二者不能同时存在):
double imag(const double& im) {...} double imag(const double im) {...} //Ambiguity【注】imag(const double& im)为signature, 不含return type.
对象模型(Object Model):关于vptr 和 vtbl
imag(const double& im)后面可以加const, const是函数签名的一部分。
所以imag(const double& im)和imag(const double& im) const两个函数可以并存。class A { public: virtual void vfunc1(); virtual void vfunc2(); void func1(); void func2(); private: int m_data1, m_data2; };class B:public A { public: virtual void vfunc1(); void func2(); private: int m_data3; };class C:public B { public: virtual void vfunc1(); void func2(); private: int m_data1 m_data4; };[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0UWljrYa-1634388189038)(https://github.com/hubojing/BlogImages/blob/master/C++程序设计之兼谈对象模型——关于vptr和vtbl.png?raw=true)]
对象模型(Object Model):关于thisTemplate Method
再谈const
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YNGEvAi6-1634388189041)(https://github.com/hubojing/BlogImages/blob/master/C++程序设计之兼谈对象模型——关于this.png?raw=true)]const object(data members不得变动) non-const object(data members可变动)
const member functions
(保证不更改data members) √ √
non-const member functions
(不保证data members不变) × √当成员函数的const和non-const版本同时存在,const object只会(只能)调用const版本,non-const object只会(只能)调用non-const版本。
const String str("hello world"); str.print();如果当初设计string::print()时未指明const,那么上行便是经由const object调用non-const member function,会出错。此非所愿。
non-const member functions可调用const member functions,反之则不行,会引发:
(VC)error C2662:cannot convert 'this' pointer from 'const class X' to 'class X &'.Conversion loses qualifiersclass template std::basic_string<…>有如下两个member functions:
charT operator[](size_type pos) const {......} reference operator[](size_type pos) {......}COW:Copy On Write
对象模型(Object Model):关于Dynamic Binding[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DeWrJbt1-1634388189045)(https://github.com/hubojing/BlogImages/blob/master/C++程序设计之兼谈对象模型——静态绑定.png?raw=true)]
动态绑定三个条件:
- 通过指针
- 虚函数
- 向上转型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wwVvzrIv-1634388189049)(https://github.com/hubojing/BlogImages/blob/master/C++程序设计之兼谈对象模型——动态绑定.png?raw=true)]
再谈new和delete ::operator new, ::operator delete, ::operator new[], ::operator delete[][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tjntHq3a-1634388189051)(https://github.com/hubojing/BlogImages/blob/master/C++程序设计之兼谈对象模型——new&delete.png?raw=true)]
重载member operator new/delete[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U1rWQgaE-1634388189056)(https://github.com/hubojing/BlogImages/blob/master/C++程序设计之兼谈对象模型——重载.png?raw=true)]
重载member operator new[]/delete[]和上图的区别在于多了一个[]
class Foo { public: void* operator new[](size_t); void operator delete[](void*, size_t); };示例, 接口[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yQKFNQIv-1634388189058)(https://github.com/hubojing/BlogImages/blob/master/C++程序设计之兼谈对象模型——示例.png?raw=true)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ywNawNvp-1634388189061)(https://github.com/hubojing/BlogImages/blob/master/C++程序设计之兼谈对象模型——示例2.png?raw=true)]
int 4字节,long 4字节,string(里面是个指针)4字节
有虚函数就多一个指针(12+4=16)Foo[5] 数组,有5个,12*5=60,第一个记录有5个元素,这个记录的size为4,60+4=64
重载new(), delete()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3dpNQINe-1634388189063)(https://github.com/hubojing/BlogImages/blob/master/C++程序设计之兼谈对象模型——示例3.png?raw=true)]我们可以重载class member operator new(),写出多个版本,前提是每一个版本的声明都必须有独特的参数列,其中第一参数必须是size_t,其余参数以new所指定的placement arguments为初值。出现于new(…)小括号内的便是所谓placement arguments。
Foo* pf = new(300, 'c') Foo;我们也可以重载class member operator delete()(或称此为placement operator delete),写出多个版本,但它们绝不会被delete调用。只有当new所调用的ctor抛出exception,才会调用这些重载版的operator delete()。它只可能这样被调用,主要用来归还未能完全创建成功的object所占用的memory。
示例class Foo { public: Foo(){cout<<"Foo::Foo()" << endl; } Foo(int){cout << "Foo::Foo(int)" << endl; throw Bad();}//class Bad{}; //故意在这儿抛出exception,测试placement operator delete.s //(1)这里就是一般的operator new()的重载 void* operator new(size_t size) { return malloc(size); } //(2)这个就是标准库已提供的placement new()的重载(的形式),(所以此处也模拟standard placement new,就只是传回pointer) void* operator new(size_t size, void* start) { return start; } //(3)这个才是崭新的palcement new void* operator new(size_t size, long extra) { return malloc(size+extra); } //(4)这又是一个placement new void* operator new(size_t size, long extra, char init) { return malloc(size+extra); } //(5)这又是一个placement new, 但故意写错第一参数的type(那必须是size_t以符合正常的operator new) //void* operator new(long extra, char init) //{ // [Error]'Operator new' takes type 'size_t'('unsigned int') as first parameter[-fpermissive] // return malloc(extra); //} //以下是搭配上述placement new的各个所谓placement delete. //当ctor发出异常,这儿对应的operator(placement) delete就会被调用. //其用于是释放对应之placement new分配所得的memory. //(1)这儿就是一般的operator delete()的重载 void operator delete(void*,size_t) {cout << "operator delete(void*, size_t)" << endl;} //(2)这是对应的(2) void operator delete(void*,void*) {cout << "operator delete(void*, void*)" << endl;} //(3)这是对应的(3) void operator delete(void*, long) {cout << "operator delete(void*, long)" << endl;} //(4)这是对应的(4) void operator delete(void*, long, char) {cout << "operator delete(void*, long, char)" << endl;} private: int m_i; };测试代码:
Foo start; Foo* p1 = new Foo; Foo* p2 = new(&start) Foo; Foo* p3 = new(100) Foo; Foo* p4 = new(100,'a') Foo; Foo* p5 = new(100) Foo(1);//ctor抛出异常 Foo* p6 = new(100,'a') Foo(1); Foo* p7 = new(&start) Foo(1); Foo* p8 = new Foo(1);[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7Vk6HPpv-1634388189066)(https://github.com/hubojing/BlogImages/blob/master/C++程序设计之兼谈对象模型——test.png?raw=true)]
ctor抛出异常,但G4.9没调用operator delete(void*, long),但G2.9确实调用了。
即使operator delete(…)未能一一对应于operator new(…),也不会出现任何报错。意思是:放弃处理ctor发出的异常。
basic_string使用new(extra)扩充申请量用于是释放对应之placement new分配所得的memory.
//(1)这儿就是一般的operator delete()的重载
void operator delete(void*,size_t)
{cout << “operator delete(void*, size_t)” << endl;}//(2)这是对应的(2) void operator delete(void*,void*) {cout << "operator delete(void*, void*)" << endl;} //(3)这是对应的(3) void operator delete(void*, long) {cout << "operator delete(void*, long)" << endl;} //(4)这是对应的(4) void operator delete(void*, long, char) {cout << "operator delete(void*, long, char)" << endl;}private:
int m_i;
};测试代码: ```cpp Foo start; Foo* p1 = new Foo; Foo* p2 = new(&start) Foo; Foo* p3 = new(100) Foo; Foo* p4 = new(100,'a') Foo; Foo* p5 = new(100) Foo(1);//ctor抛出异常 Foo* p6 = new(100,'a') Foo(1); Foo* p7 = new(&start) Foo(1); Foo* p8 = new Foo(1);[外链图片转存中…(img-7Vk6HPpv-1634388189066)]
ctor抛出异常,但G4.9没调用operator delete(void*, long),但G2.9确实调用了。
即使operator delete(…)未能一一对应于operator new(…),也不会出现任何报错。意思是:放弃处理ctor发出的异常。
basic_string使用new(extra)扩充申请量[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uwxPYzi8-1634388189070)(https://github.com/hubojing/BlogImages/blob/master/C++程序设计之兼谈对象模型——basic_string.png?raw=true)]



