前言
应具备的基础
- 是上一篇博文“面向对象程序设计”的续集
- 本文将探讨上文未讨论的主题
目标
- 在先前培养正规、大器的编程素养上,继续探讨更多技术。
- 泛型编程(Generic Programming)和面向对象编程(Object-Oriented Programming)虽然分属不同思维,但它们正是C++的技术主线。本文也讨论template(模板)。
- 深入探索面向对象之继承关系(inheritance)所形成的对象模型(Object Model),包括隐藏于底层的this指针,vptr指针(虚指针),vtbl(虚表),virtual mechanism(虚机制),以及虚函数(virtual functions)造成的polymorphism(多态)效果。
将获得的代码
Test-Cpp.cpp
C++编译器
conversion function, 转换函数
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;
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 d2 = f + 4;
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;
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;
conversion function, 转换函数 proxy
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)); }
...
pointer-like classes, 关于智能指针
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);
sp->method();
相当于
px->method();
pointer-like classes, 关于迭代器
template<class T, class Ref, class Ptr>
struct __list_iterator
{
typedef __list_iterator<T, Ref, Ptr> self;
typedef Ptr pointer;
typedef Ref reference;
typedef __list_node<T>* 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; }
};
使用:
list<Foo>::iterator ite;
...
*ite;
ite->method();
funciton-like classes, 所谓仿函数
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) {}
......
};
标准库中仿仿函数的奇特模样
template <class T>
struct identity : public unary_function<T, T>
{
const T&
operator() (const T& x) const { return x; }
};
template <class Pair>
struct select1st : public 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 : public 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 : public binary_function<T, T, T>
{
T operator()(const T& x, const T& y) const { return x + y; }
};
template <class T>
struct minus : public binary_function<T, T, T>
{
T operator()(const T& x, const T& y) const { return x - y; }
};
template <class T>
struct equal_to : public binary_function<T, T, bool>
{
T operator()(const T& x, const T& y) const { return x == y; }
};
template <class T>
struct plus : public binary_function<T, T, bool>
{
T operator()(const T& x, const T& y) const { return x < y; }
};
标准库中,仿函数所使用的奇特的base classes
template <class Arg, class Result>
struct unary_function
{
typedef Arg argument_type;
typedef Result result_type;
};
template <class Arg1, class Arg2, class Result>
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<iostream>
#include<memory>
namespace jj01
{
void test_member_template()
{ ...... }
}
#include<iostream>
#include<list>
namespace jj02
{
template<typename T>
using Lst = list<T, allocator<T>>;
void test_template_template_param()
{ ...... }
}
使用:
int main(int argc, char** argv)
jj01::test_member_template();
jj02::test_template_template();
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&);
};
使用:
{
complex<double> c1(2.5, 1.5);
complex<int> c2(2, 6);
...
}
function template, 函数模板
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<
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, 成员函数
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) {}
};
class Base1{};
class Derived1:public Base1{};
class Base2{};
class Derived2:public Base2{};
pair<Derived1, Derived2>p;
pair<Base1, Base2>p2(p);
pair<Base1, Base2>p2(pair<Derived1, Derived2>());
template<typename _Tp>
class shared_ptr:public __shared_ptr<_Tp>
{
...
template<typename _Tp1>
explicit shared_ptr(_Tpl* __p)
:__shared_ptr<_Tp>(__p){}
...
};
Base1* ptr = new Derived1;
shared_ptr<Base1>sptr(new Derived1);
specialization, 模板特化
【注】特化反义词:泛化
泛化
template <class Key>
struct hash{ };
特化
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);
泛化又叫full specialization,全泛化,对应偏特化。
patial specialization, 模板偏特化——个数的偏
template<typename T, typename Alloc=...>
class vector
{
...
};
绑定
template<typename Alloc=...>
class vector<bool, Alloc>
{
...
patial specialization, 模板偏特化——范围的偏
template <typename T>
class C
{
...
};
【注】上下的T不是一个T
template <typename T>
class C<T*>
{
...
};
这样写也可以
template <typename U>
class C<U*>
{
...
};
使用:
C<string> obj1;
C<string*> obj2;
template template parameter, 模板模板参数
template<typename T,
template <typename T>
class Container
>
class XCls
{
private:
Container<T> c;
public:
......
};
template<typename T>
using Lst = list<T, allocator<T>>;
XCls<string, list> mylst1;
XCls<string, Lst> mylst2;
template<typename T,
template <typename T>
class SmartPtr
>
class XCls
{
private:
SmartPtr<T> sp;
public:
XCls():sp(new T) { }
};
XCls<string, shared_ptr> p1;
XCls<string, unique_ptr> p2;
XCls<int, weak_ptr> p3;
XCls<long, auto_ptr> p4;
这不是template template parameter
template <class T, class Sequence = deque<T>>
class stack
{
friend bool operator== <> (const stack&, const stack&);
friend bool operator< <> (const stack&, const stack&);
protected:
Sequence c;
......
};
使用
stack<int> s1;
stack<int, list<int>> s2;
关于C++标准库
容器
Sequence containers
array vector deque forward_list list
Container adaptors
stack queue priority_queue
Associative containers
set multiset map multimap
Unordered associative con
unordered_set unordered_multiset unordered_map unordered_multimap
算法
…
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
Merge
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"
#include <iostream>
using namespace std;
int main()
{
cout<<__cplusplus<<endl;
return 0;
}
Dev-C++ 5
#include <iostream>
int main()
{
std::cout<<__cplusplus;
}
如果是199711,则不支持C++11,需修改编译器 如果是201103,则支持C++11
variadic templates(since C++11) 数量不定的模板参数
void print()
{
}
template<typename T, typename... Types>
void print(const T& firstArg, const Type&... args)
{
cout<<firstArg<<endl;
print(args...);
}
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
42
auto(since C++11)
过去:
list<string> c;
...
list<string>::iterator ite;
ite = find(c.begin(), c.end(), target);
现在:
list<string> c;
...
auto ite = find(c.begin(), c.end(), target);
错误:
list<string> c;
...
auto ite;
ite = find(c.begin(), c.end(), target);
ranged-base for(since C++11)
for(decl : coll)
{
statement
}
for(int i : {2, 3, 5, 7, 9, 13, 17, 19})
{
cout<< i << endl;
}
vector<double> vec;
...
for(auto elem : vec)
{
cout << elem << endl;
}
for(auto& elem : vec)
{
elem *= 3;
}
reference
int x=0;
int* p = &x;
int& r = x;
int x2 = 5;
r = x2;
int& r2 = r;
从内存上看, [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bWdLqDZF-1634388189033)(https://github.com/hubojing/BlogImages/blob/master/C++程序设计之兼谈对象模型——reference.png?raw=true)]
注意:
- sizeof? == sizeof(x)
- &x = &r;
object和其reference的大小相同,地址也相同(全都是假象) Java里头所有变量都是reference
typedef struct Stag{int a, b, c, d;} S;
int main()
{
double x = 0;
double* p = &x;
double& r = x;
cout << sizeof(x) << endl;
cout << sizeof(p) << endl;
cout << sizeof(r) << endl;
cout << p << endl;
cout << *p << endl;
cout << x << endl;
cout << r << endl;
cout << &x << endl;
cout << &r << endl;
S s;
S& rs = s;
cout << sizeof(s) << endl;
cout << sizeof(rs) << endl;
cout << &s << endl;
cout << &rs << endl;
}
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) {...}
【注】imag(const double& im)为signature, 不含return type. imag(const double& im)后面可以加const, const是函数签名的一部分。 所以imag(const double& im)和imag(const double& im) const两个函数可以并存。
对象模型(Object Model):关于vptr 和 vtbl
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):关于this
Template Method [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YNGEvAi6-1634388189041)(https://github.com/hubojing/BlogImages/blob/master/C++程序设计之兼谈对象模型——关于this.png?raw=true)]
再谈const
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 qualifiers
class 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 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3dpNQINe-1634388189063)(https://github.com/hubojing/BlogImages/blob/master/C++程序设计之兼谈对象模型——示例3.png?raw=true)]
重载new(), delete()
我们可以重载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();}
void* operator new(size_t size)
{
return malloc(size);
}
void* operator new(size_t size, void* start)
{
return start;
}
void* operator new(size_t size, long extra)
{
return malloc(size+extra);
}
void* operator new(size_t size, long extra, char init)
{
return malloc(size+extra);
}
void operator delete(void*,size_t)
{cout << "operator delete(void*, size_t)" << endl;}
void operator delete(void*,void*)
{cout << "operator delete(void*, void*)" << endl;}
void operator delete(void*, long)
{cout << "operator delete(void*, long)" << endl;}
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);
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)]
|