C++ 学习4 泛型编程 面向对象

  • Conversion function
  • non-explicit one argument ctor
  • Pointer liker classes 智能指针,迭代器
  • Function like classes Functor 仿函数
  • namespace
  • class template function template
  • specialization, Partial spectialization
  • template template parameter 模板模板参数
  • STL
  • C++11 :Varadic template, auto, ranged-base for
  • 引用 refernce
  • 复合&继承关系下的构造与析构

Conversion function 转换函数

class Fraction
{
public:
    Fraction(int num, int den=1):m_num(num), m_den(den) { }
    operator double() const {
    return (double) m_num/m_den ;
}
private:
    int m_num, m_den;
};
Fraction f(3,4);
double d = 4 + f;//调用operator double() 将f转换为double。

此处不写return type 由于已经指定double;
全局函数operator + (left + right) <–> left + right

non-explicit-one-argument ctor

class Fraction
{
public:
        Fraction (int num, int den = 1): m_num(num), m_den(den) { }
Fraction operator+ (const Fraction& f) {
    return Fraction (...);
}   
private:
    int m_num, m_den;
};
Fraction f(3, 5);
Fraction d2 = f + 4;//调用non-explicit ctor 将4转为Fraction(4,1)然后调用operator +

注意此处与conversion function区别 如果定义d2 为double 类型与上面相同则无法转换。
注意 operator + (单参数) <–> f + 4;
左 + 右
无法 调用 4 + f;

如果同时定义转换函数和单参数non-explicit 构造函数将会造成错误编译器无法选择。
如果构造函数前面加上explicit 关键字 构造函数无法自动转化4 为 Fraction 类型 此处也会产生错误。
STL中conversion函数的应用

template <class Alloc>
class vector<bool,Alloc>
{
public:
typedef __bit_reference refernce;
protected:
refernce operator[](size_type n) {
    return *(begin() + difference_type(n));
}
}
...
struct __bit_refernce {
    unsigned int*p;
    unsigned int mask;
public:
    operator bool() const {
    return !(!(*p & mask));
}
} 

此处reference 可以自动转换为bool 类型 。

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
{
    method() {}
};

shared_ptr<Foo> sp(new Foo);
Foo f(*sp);
sp->method(); --> px->method

即必须重载 类内操作符 operator * 和operator-> 注意此处-> 符号并不会消耗。
STL boost
unique_ptr scoped_ptr 独占指针对象,并保证指针所指对象生命周期与其一致
shared_ptr shared_ptr 可共享指针对象,可以赋值给shared_ptr或weak_ptr。
指针所指对象在所有的相关联的shared_ptr生命周期结束时结束,是强引用。
weak_ptr weak_ptr 它不能决定所指对象的生命周期,引用所指对象时,需要lock()成shared_ptr才能使用。
参考:http://blog.csdn.net/xt_xiaotian/article/details/5714477
https://my.oschina.net/hevakelcj/blog/465978
2. 迭代器

template<class T>
struct __list_node {
    void* prev;
    void* next;
    T data;
};
template<class T, class Ref, class Ptr>
struct __list_iterator {
    typedef __list_iterator <T, Ref, Ptr> self;
    typedef Ptr Pointer;
    typedef Ref refernce;
    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;}
    refernce 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--() {
self tmp = *this;--*this; return tmp;}
}
    (1)前置++运算符的重载方式:
    成员函数的重载: 函数类型& operator++()
    友元函数的重载:friend 函数类型& operator++(类类型& )

    (2)后置++运算符的重载方式:
    成员函数的重载:函数类型& operator++(int)
    友元函数的重载:friend 函数类型& operator++(类类型&, int)

1、输入迭代器:只读,一次传递
为输入迭代器预定义实现只有istream_iterator和istreambuf_iterator,用于从一个输入流istream中读取。一个输入迭代器仅能对它所选择的每个元素进行一次解析,它们只能向前移动。一个专门的构造函数定义了超越末尾的值。总是,输入迭代器可以对读操作的结果进行解析(对每个值仅解析一次),然后向前移动。
2、输出迭代器:只写,一次传递
这是对输入迭代器的补充,不过是写操作而不是读操作。为输出迭代器的预定义实现只有ostream_iterator和ostreambuf_iterator,用于向一个输出流ostream写数据,还有一个一般较少使用的raw_storage_iterator。他们只能对每个写出的值进行一次解析,并且只能向前移动。对于输出迭代器来说,没有使用超越末尾的值来结束的概念。总之,输出迭代器可以对写操作的值进行解析(对每一个值仅解析一次),然后向前移动。
3、前向迭代器:多次读/写
前向迭代器包含了输入和输出迭代器两者的功能,加上还可以多次解析一个迭代器指定的位置,因此可以对一个值进行多次读/写。顾名思义,前向迭代器只能向前移动。没有为前向迭代器预定义迭代器。
4、双向迭代器:operator–
双向迭代器具有前向迭代器的全部功能。另外它还可以利用自减操作符operator–向后一次移动一个位置。由list容器中返回的迭代器都是双向的。
5、随机访问迭代器:类似于一个指针
随机访问迭代器具有双向迭代器的所有功能,再加上一个指针所有的功能(一个指针就是一个随机访问迭代器),除了没有一种“空(null)”迭代器和空指针对应。基本上可以这样说,一个随机访问迭代器就像一个指针那样可以进行任何操作,包括使用操作符operator[]进行索引,加某个数值到一个指针就可以向前或者向后移动若干个位置,或者使用比较运算符在迭代器之间进行比较。
参考:http://www.tuicool.com/articles/ZJ36Rf
http://blog.csdn.net/shawvichan/article/details/17639763
http://www.cnblogs.com/yc_sunniwell/archive/2010/06/25/1764934.html

Function-like classes

仿函数 functor

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 Arg,class Result>
struct unary_function {
typedef Arg argument_type;
typedef Result result_type;
};
template <class Arg1, classArg2, class result>
struct binary_function {
    typedef Arg1 first_argument_type;
    typedef Arg2 second_argument_type;
    typedef Result result_type;
};

重载operator() 实现类似函数调用方式。参考python。

参考:http://blog.csdn.net/cracker_zhou/article/details/51627811
http://blog.csdn.net/tianshuai1111/article/details/7687983
http://blog.csdn.net/Robin__Chou/article/details/49329985

namespace

#include <iostream>
#include <memory>//shared_ptr
using namespace std;
namespace jj01
{
void test_member_template()
{}
}//namespace
//-------------
#include <iostream>
#include <list>
namespace jj02
{
template<typename T>
using Lst = list<T, alloctor<T>>
void test_template_param(){...}
}//namespace
//-------------
int main(int argc,char** argv)
{
jj01::test_memeber();
jj02::test_template_param();
}

class template, function template, member template

1.class template

template<tyoename 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;    
};
{
    complex<double> c1(1.5, 2.5);
    compex<int> c2(2, 6);
}
  1. function 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), _we(we) {}
    bool operator < (const stone& rhs) const {return _we < rhs._we;}
private:
    int _w, _h, _we;
};

stone r1(2,3), r2(2,3),r3;
r3 = min(r1, r2);

此处编译器会对函数模板进行推导 推导的结果是T类型为stone 调用stone:: operator <.
3. member template 成员模板

template <class T1, class T2>
struct pair {
    typedef T1 first_type;
    typedef T2 second_type;
    T1 first;
    T2 second;
    pair():fist(T1()), second(T2()) {}
    pair(const T1& a, const T2& b): fist(a), second(b) {}
    template<class U1, clas U2>
    pair(const pair<U1, U2>& p):first(p.first), second(p.second) {}
}
//可以用于继承类中
class Base1{};
class Derived1:public Base1 {};
class Base2{};
clss Dedived2:publlic Base2 {};
pair<Derived1,Deived2> p;
pair<Base1, Base2> p2(p);
pair<Base1, Base2> p2(pair<Derived1, Derived2>());
//Derived1是Base1, Derived2是Base2,所以可以。
//example 2
template <typename _Tp>
class shred_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 Derived);//模拟up-cast

如果要在外面定义成员模板,必须包含两个模板形参表,类模板形参和自己的模板形参。首先是类模板形参表,然后是自己的模板形参表。
类似:
template<class T, class Alloc> template <class I>
void MyVector<T, Alloc>::assign(I first, I last)
{
cout<<"assign"<<endl;
}

参考:http://blog.csdn.net/wuzhekai1985/article/details/6654034

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;}
};
  1. patial specialization, 模板偏特化 – 个数偏
template <typename T, typename Alloc = ...>
class vector
{
...
};
template<typename Alloc = ...>
class vector<bool, Alloc>
{...};
//bool 绑定 T
  1. 范围偏
template <typename T>
class C
{...};
template <typename T>
classC<T*>
{...};
c<string> obj1;
c<string*> obj2;

函数没有偏特化只有全特化。
参考:
http://www.jb51.net/article/56004.htm
http://blog.csdn.net/thefutureisour/article/details/7964682/
http://blog.csdn.net/gatieme/article/details/50953564

template template parameter 模板模板参数

template <typename T, template <typename T> class Container>
calss XCls
{
private:
    Container<T> c;
public:
...
};
template <typename T>
using Lst = list<allocator<T>>;
XCls<string, list> mylst1;//wrong
XCls<string, Lst> mylst2;//correct
//----------
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<double, unique_ptr> p2;//wrong
XCls<int, weak_ptr> p3;//wrong
XCls<long, auto_ptr> p4;
//----以下不是template template parameter
template <class T, class Sequnce = deque<T>>
class stack {
    friend bool operator == <> (const stack&, const stack&);
    friend bool operatir < <> (const stack&, const stack&);
protected:
    Sequence c;//底层容器
};
stack<int> s1;
stack<int, list<int>> s2;

模板模板参数就是将一个模板作为另一个模板的参数。 所以最后一个例子不是。
另外还有无类型模板 :

template<typename T,const T EMPTY> 
class Grid  {...}
Greid <int, 10> my grid;

指针和引用模板参数必须指向所有翻译单元中都可用的全局变量。对于这些类型的变量,相应的技术术语就是带有外部连接的数据。
使用extern声明即可。
如:

template<typename T ,const T& EMPTY>  
class Grid  
{...};  
extern const int emptyInt=0;  
Grid<int,emptyInt> myIntGrid; 

对于初始化我们还可以使用“零初始化”即 T().
无类型参数仅限于int、enmu、指针和引用
当为
参考:http://blog.csdn.net/pcliuguangtao/article/details/6449804

STL

容器,迭代器, 算法, 仿函数,待补充。

C++ 11 补充

  1. Variadic template 数量不定模板参数
void print (){}
template <typename T, typename... Types>
void print (const T& firstArg, const Types&...args)
{
    cout << firstArg<<endl;
    print(args...)
}

此处用于tempalte paramters 就是template parameters pack (模板参数包) 个数可以用sizeof…(args) 来获得。最后sizeof…(args) = 0 时调用第一个重载的空print函数什么都不做返回。

参考:http://www.cnblogs.com/tekkaman/p/3501122.html
http://blog.csdn.net/wag2765/article/details/50581469
2. auto
C++引入auto关键字主要有两种用途:一是在变量声明时根据初始化表达式自动推断该变量的类型,二是在声明函数时作为函数返回值的占位符。

list<string> c;
list<string>::iterator ite;
ite= find(c.begine(),c.end9),target);
//可以用auto 代替
auto = find(...)
//但是不能写为auto ite;
ite = find(...)
//此时编译器无法推断auto类型。
//作为函数返回值占位符时,auto主要与decltype关键字配合使用,作为返回值类型后置时的占位符。此时,关键字不表示自动类型检测,仅仅是表示后置返回值的语法的一部分。
template<class T, class U>
auto add(T t, U u) -> decltype(t + u) 
{
    return t + u;
}

参考:http://blog.csdn.net/Xiejingfa/article/details/50469045
http://www.2cto.com/kf/201404/293503.html
3.Range-base for

for (decl:coll){}

for (int i : {1,2,3}){
    cout << i << endl;
}
vector<double> vec;

for (auto elem :vec) {
    cout << elem << endl;//按值传递
}
for (auto& elem: vec) {
    elem* = 3;//按引用传递可以改变包内内容
}

Reference引用

1.引用不能重新赋值
2. 引用不能单独声明
3. object 和其引用大小地址均相同
4. 引用不构成重载。 const 函数构成重载即func() const {} 构成重载由于其是函数签名的一部分。
5. sizeof(值) = sizeof(引用)

复合与继承关系下的构造与析构

构造由内而外
析构相反由外而内
构造先base 再component 最后自己。
Derived::Derived():Base():COmponent(){}
析构先自己再component最后base
Derived::~Derived(){…~Component,~Base()}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值