GeekBand C++ week3

一、导论


1.I和II的区别

I:C++面向对象程序设计
II: C++程序设计兼谈对象模型

勿在浮沙筑高台

2.C++两大技术主线

Generic Programming 泛型编程 template
Object-Oriented Programming 面向对象编程 class

二、转换函数


I:由其它对象向该类型对象转换
II:由该类型对象向其它类型对象转换

convertion function

转型函数
语法结构:operator + typename(只要是之前声明的,包括基本和构造类型) + () + const

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; //分母
};

转换函数样例: operator double() const
特征:

  • 静态函数,表不改变对象内数据。
  • 无返回类型声明,默认目标类型为返回类型。有效的防止声明和返回值(目标类型不对应)
  • 无参数,转换不需要参数。
Fraction f(3, 5);
double d=4+f; //调用operator double() 将 f 转为0.6

调用示例:
在这段代码执行时,先构造一个分子为3,分母为5的分数。然后,编译器搜寻可用函数(可以使这行通过的函数),

  1. operator + (double/int) 整数可以转换为浮点数
  2. operator double (fraction) 以满足operator +对左右操作数的类型要求。

non-explicit-one-argument ctor

explicit是声明符,
没有explicit声明的单实参构造函数,可以只传入一个实参。
通过隐式调用,以构造的方式,把其它类型转换为该类型。

class Fraction
{
public:
    Fraction(int num, int den=1) : m_numerator(num), m_denominator(den) { } //non-explicit-one-argument ctor,只有一个参数有默认值
    Fraction operator + (const Fraction& f) {
        return Fraction(......)
    }
private:
    int m_numerator; //分子
    int m_denominator; //分母
};

区分argument和parameter argument:实参;parameter:参数,形参

Fraction f(3, 5);
Fraction d2=f+4; //调用 non-explicit ctor将 4 转为 Fraction(4, 1)
                         //然后调用operator+

operator +需要左右都为Fraction类型,于是搜寻转换函数,将4转换为4/1进行运算

convertion 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; //分母
};

//test
Fraction f(3, 5);
Fraction d2=f+4; //[Error] ambiguous 二义的

出现两种路径:将4转为Fraction 4/1,然后用类内+计算出d2;将f转为double 0.6,然后用全局+,在赋值时调用单参构造函数,将double和转换为Fraction 4/1。
编译器在面对两条路径时不能比对优劣,会报错。

explicit-one-argument ctor

explicit 明确的,清楚的;一般修饰在构造函数前,表示构造函数需要显式调用(即以构造方式调用)。

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; //分母
};

//test
Fraction f(3, 5);
Fraction d2=f+4; //[Error] conversion from 'double' to 'Fraction' request

处理后,operator = 左右类型不同。因为4无法通过隐式调用构造函数变为Fraction 4/1,只能将 f 转换为double 0.6,而在赋值时,隐式调用再次失败,左右类型不同,赋值运算失败。

综合:设计模式 Proxy 代理

template<class Alloc>
class vector<bool, Alloc> //对容器vector进行模板偏特化
{
public:
    typedef __bit_reference;
protected:
    reference operator[ ] (size_type n) { //传出第n个位置的bool值,却以reference传回,形成代理
        return *(begin() + difference_type(n));
}
...
struct __bit_reference {
    unsigned int* p;
    unsigned int mask;
    ...
public:
    operator bool() const { return !(!(*p & mask)); //reference代理bool,自然需要转换函数
    ...
}

三、pointer-like template


智能指针

template<class T>
class shared_ptr
{public:
    //操作符重载
    T& operator* () const //dereference解参考,取指针所指的东西
    { return *px; }   //通用写法
    T* operator-> () const   //通过对象调用成员
    { return px; }
    //构造函数
    shared_ptr(T* p) : px(p) { }

private:
    T* px; //指针成员
    long* pn;
   ...
};

典型组成:指针成员+操作符重载
’->‘ 的特殊行为:‘->’ 符号作用的结果,继续受 ‘->' 作用。

迭代器

用于遍历容器,指针代表容器内的一个元素
含有++,--操作,涉及指针的移动

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* () ); }   //为了在双向链表中取得data域的地址

    self& operator++ () { node = (link_type)(*(node).next); return *this; }
    self operator++ (int) { self tmp = *this; ++*this; return *this; }
    self& operator-- () { node = (link_type)((*node).next); return *this; }
    self operator-- (int) { self tmp = *this; --*this; return *this; }
};

四、function-like template


'()' function call operator函数
如果一个东西可以接受 '()' 的操作,则称为function / function-like

functer仿函数

//GNU C添加
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::first_type> {    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) { }
    ...
};
//base classes
template <class Arg, class Result>
struct unary_function {
    typedef Arg argument_type;
    typedef Result result_type;
};

在标准库中仿函数普遍继承一些仅含typedef,size为0的父类(base classes)

五、namespace


建立名称区隔,解决软件开发过程中名称冲突的问题

using namespace std;
//----------------------------------------
#include <iostream>
#include <memory> //sharedptr
namespace jj01
{
void test_member_template()
{ ... }
} //namespace

int main(int argc, char** argv)
{
    jj01::test_member_template();
}

六、template 模板


抽出认为可以被使用者任意指定的类型,以实现代码复用

class template 类模板

template<typename T>
class complex
{
public:
    complex (T r = 0, T i = 0) : re (r), im (i) { }
    comple& operator+= (const complex&);
    T real () const { return re; }
    T imag () const { return im; }
private:
    T re, im;
    ...
}
{
   complex<double> c1(2.5, 1.5);
   complex<int> c2(2, 6);
}

function template 函数模板

template <class T>
inline
const T& min (const T& a, const T& b)
{
    return b < a ? b : a;
}
stone r1(2, 3), r2(3, 3), r3;
r3 = min(r1, r2);
}

函数模板在调用时不需显式声明参数类型,编译器会根据实参类型进行推导(argument deduction)
编译器编译模板到目标文件只是半成品,也只保证模板本身的语法通过性,链接时根据调用搜寻是否具有合法操作路径,决定能否生成可执行代码

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) { }
    //member template
    template<class U1, class U2>
    pair(const pair<U1, U2>& p) : first(p.first), second(p.second) { } //满足类型转换条件下建立一种拷贝构造函数,让构造函数更有弹性
};

满足类型转换条件(尤其是将子类向父类转化,up-cast)下建立一种拷贝构造函数,让构造函数更有弹性

(full) specialization 模板特化

与泛化相对,模板本身就是泛化,可能会需要对某些特殊类型进行特殊设计

//泛化标准库
template<class key>
struct hash { };
//特化
template<>
struct hash<char> {
    size_t operator() (char x) const { return x; }
};
template<int> 
struct hash<int> {
    size_ operator() (int x) const { return x; }
};
template<>
struct hash<long> {
    size_t operator() (long x) const { return x; }
};

特化可以有无数个版本

partial specialization 偏(局部)特化

个数上的偏

template<typename T, typename Alloc=...>
class vector
{
...
};
templaste<typename Alloc=...>
class vector<bool, Alloc> //将T与bool绑定,实现只针对一个进行特化
{
..
}

范围的偏

//泛化
template <typename T>
class C
{
...
};
//特化
template <typename T> //可以完全将T替换成U,完全成立
class C<T*>                   //针对任意类型的指针进行特化
{
...
};

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, Lst> mylst;   //注意,容器具有多于一个参数时,会发生编译错误,需要以上处理

模板<>中typename和class共通,早先没有typename关键字,借用class

七、C++11的三个特性


语法糖(Syntactic sugar) 指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。

variadic templates(since C++11) 数量不定的模板参数

void print() //当后边一包已经为空时,调用该函数,以完整程序
{
}
template<typename T, *typename... Types*>
void print (const T& firstArg, const Types&... args)
{
    cout<<firstArg<<endl;
    print(args...);
}

将函数模板的参数分成一个和一包(包中参数具有任意类型和个数)
sizeof...(args) 查看args的个数

关键字auto(since C++11)

//正常版
list<string> c;
...
list<string>::iterator ite;
ite = find(c.begin(), c.end(), target);
//auto 推导版
list<string> c;
...
auto ite = find(c.begin(), c.end(), target);

用auto方式声明后,编译器可通过赋值来推导ite的类型,减少了编码工作量。
在编程过程中尽量明确类型的使用是编程素养的体现。

ranged-base for(since C++11)

for(decl :coll) {
    statement
}

将一个容器中的元素,逐一赋值给decl,至容器中最后一个元素也完成对decl的赋值。

八、reference


变量有三种,value,pointer,reference

int x=0; //int 4byte
int* p = &x; //32bit OS pointer 4byte
int& r = x; //r 代表 x,现在 r=x=0。此时 r 应逻辑上看作整数,只是实现上是指针
int x2 = 5;

r = x2; //r 不能重新代表其他物体。现在r,x都是5

声明reference一定有初值,且设定后不能改变,而指针可变。
object和其reference的大小相同,地址也相同。
reference通常不用于声明变量,而用于参数类型(parameters type)和返回类型(return type)的描述。
reference就是一种漂亮的pointer。pass by reference保持了指针传递速度快的同时,保持了和pass by value相同的函数接口。因此同函数名,同参数个数、类型,一个是reference,一个是value,为same signature。C++的函数签名,不包含返回类型。

(面向对象部分将集中在C++ week 4 中讲述)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • 一 . 关于类型转换 1. conversion function (转换函数) —— “转出去” 类型转换函数的...
    胖胖核桃阅读 171评论 0 1
  • 1.导读 勿在浮沙筑高台   本课程既有面向对象,也有泛型编程。是上门课程的续集,主要讲上门课程没有提到的东西。 ...
    hui1429阅读 320评论 0 1
  • C++运算符重载-下篇 本章内容:1. 运算符重载的概述2. 重载算术运算符3. 重载按位运算符和二元逻辑运算符4...
    Haley_2013阅读 1,402评论 0 49
  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy阅读 9,447评论 1 51
  • 之前几次,对C++中的一些核心话题进行了一些梳理,主要都是集中在关于面向对象的思想方面。我通过部分故事的思路,结合...
    故事狗阅读 301评论 0 1