virtual

virtual void fun()=0
纯虚函数,这种函数在派生类中必须重写,否则该派生来也是个虚基类

别名指定

这点就是最开始看到的source code。在C++11中提出了通过using指定别名。

例如上面source code 中:

using value_type = _Ty

以后使用value_type value; 就代表_Ty value;

这个让我们想起了typedef,using 跟typedef有什么区别呢?哪个更好用些呢?

例如:

typedef std::unique_ptr<std::unordered_map<std::string, std::string>> UPtrMapSS;

而C++11中:

using UPtrMapSS = std::unique_ptr<std::unordered_map<std::string, std::string>>;


或许从这个例子中,我们是看不出来明显的好处的(而于我来说,以一个第三者的角度,这个例子也难以说服我一定要用C++11的using)。
再来看下:

typedef void (*FP) (int, const std::string&);

若不是特别熟悉函数指针与typedef的童鞋,我相信第一眼还是很难指出FP其实是一个别名,代表着的是一个函数指针,而指向的这个函数返回类型是void,接受参数是int, const std::string&。那么,让我们换做C++11的写法:

using FP = void (*) (int, const std::string&);

我想,即使第一次读到这样代码,并且知道C++11 using的童鞋也能很容易知道FP是一个别名,using的写法把别名的名字强制分离到了左边,而把别名指向的放在了右边,比较清晰。


而针对这样的例子,我想我可以再补充一个例子:

    typedef std::string (Foo::* fooMemFnPtr) (const std::string&);
     
    using fooMemFnPtr = std::string (Foo::*) (const std::string&);

从可读性来看,using也是要好于typedef的。

那么,若是从可读性的理由支持using,力度也是稍微不足的。来看第二个理由,那就是举出了一个typedef做不到,而using可以做到的例子:alias templates, 模板别名。

    template <typename T>
    using Vec = MyVector<T, MyAlloc<T>>;
     
    // usage
    Vec<int> vec;

这一切都会非常的自然。


那么,若你使用typedef来做这一切:

    template <typename T>
    typedef MyVector<T, MyAlloc<T>> Vec;
     
    // usage
    Vec<int> vec;

当你使用编译器编译的时候,将会得到类似:error: a typedef cannot be a template的错误信息。

从引用传递到设计模式 (上)

1  值传递

  值传递是 拷贝实参的值 传给形参,常用于“小对象” (small objects)

复制代码

int fact(int val)   // factorial of val 
{
    int ret = 1; 
   
    while (val > 1)  // assign ret * val to ret and decrement val
        ret *= val--; 
    
    return ret;
}

复制代码

  调用下面函数,便是值传递:

cout << "5! is " << fact(5) << endl;

  小对象一般为:内置类型(built-in types),STL迭代器,函数对象类型(function object types)

  只包含一对数据(x,y) 的 Point 类,也视为小对象

void Point::operator+=(Point delta); // pass-by-value

 

2  引用传递

  引用传递不涉及拷贝,传递给形参的是实参变量的引用,其有两个优点:更高效和防切断,常用来传递“大数值” (large values) 

2.1  更高效

  基类 Person,派生类 Student

复制代码

class Person{
private:
    std::string name;
    std::string address;
};

class Student: public Person{
private:
    std::string schoolName;
    std::string schoolAddress;
};

复制代码

  现有一个验证学生身份的函数,形参为值传递,则拷贝实参给形参的代价是:

  调用 Person 构造函数一次,基类内 string 型数据成员的构造函数两次;Student 构造函数一次,派生类内 string 型数据成员两次;最后还会调用相应的析构函数六次,共计十二次调用,自然效率低下。

  而引用传递,并不涉及拷贝操作,故显著的提高了效率。

bool validateStudent(Student s);    // pass-by-value 

bool validateStudent(const Student& s);  // pass-by-reference-to-const 

2.2  防切断

  下面的例子中,派生类 WindowWithScrollBars 中,重写了基类 Window 的虚函数 display

复制代码

class Window {
public: 
    std::string name() const;        // return name of window
    virtual void display() const;    // draw window and contents
};

class WindowWithScrollBars : public Window {
public:
    virtual void display() const;
};

复制代码

  在 printNameAndDisplay 函数中,调用了 dispaly 函数,而形参若采用值传递方式,则会发生“切断” (slicing),即 wwsb 调用的是 Window::display()

  因为在 printNameAndDisplay 中,并不修改传递进来的参数,假如采用 pass-by-const-reference 的形式,则会避免“切断”的发生

复制代码

void printNameAndDisplay(Window w)
{
    std::cout << w.name();
    w.display();
}

// WindowWithScrollBars object will be sliced off
WindowWithScrollBars  wwsb;
printNameAndDisplay(wwsb);

复制代码

 

3  动态绑定

  上面"切断"的例子,实际上涉及的是 C++ 的动态绑定机制 (dynamic binding), 而动态绑定的一个关键就是引用传递,看下面例子:

复制代码

class Quote {
public:
    std::string isbn() const;
    virtual double net_price(std::size_t n) const;
};

class Bulk_quote : public Quote { 
public:
    double net_price(std::size_t) const override;
};

复制代码

  在 cal_total 函数中,需要调用 net_price,采用 pass-by-const-reference 形式

double cal_total(const Quote &item, size_t n)  // calculate the price
{
    double ret = item.net_price(n);
    return ret;
}

  调用 Quote::net_price 还是 Bulk_quote::net_price, 取决于传递进来的参数

// basic is type Quote; bulk is type Bulk_quote
cal_total(basic, 20); //  Quote::net_price
cal_total(bulk, 20);  //  Bulk_quote::net_price

   C++ 的动态绑定也叫“迟邦定”,它使程序直到运行时,才基于引用或指针绑定的对象类型,来选择调用哪个虚函数

 

4  设计模式 

    前面说到,动态绑定的一个关键是引用传递。它还有另一个关键 — 虚函数,例 2.2 中的 display 为虚函数,例 3 中的 net_price 同样也是虚函数。

 4.1  模板方法

   有一种编程惯例叫做 NVI (non-virtural interface) — 非虚拟接口: 将所有的公有函数 (public) 声明为非虚拟的,也即虚函数声明为私有或保护 (private or protected)

   该 NVI 惯例的实现,是通过一种设计模式 —— 模板方法 (template method) 来完成的

 

 1)  AbstractClass: TemplateMethod 为非虚成员函数 (public),函数体内调用 PrimitiveOperation1 和 PrimitiveOperation2 两个虚函数(protected)

 2)  ConcreteClass: 继承自 AbstractClass, 重写了两个虚函数 PrimitiveOperation1 和 PrimitiveOperation2

4.2  代码实现

  按该模式则例 3 中 Quote 里,可将 cal_total 声明为公有非虚成员函数,net_price 则声明为保护型,Bulk_quote 公有继承自 Quote,且重写虚函数 net_price

  但在实际中,只有当 cal_total 内至少包含两个类似 net_price 的操作函数 (比如先调用 net_price 再 print_price),才有使用设计模式的必要

  下面是模板方法模式的简单示例:

复制代码

class AbstractClass {
public:
    void TemplateMethod();
protected:
    virtual void PrimitiveOperation1() = 0;
    virtual void PrimitiveOperation2() = 0;
};

class ConcreteClass : public AbstractClass {
protected:
    void PrimitiveOperation1() override;
    void PrimitiveOperation2() override;
};

void AbstractClass::TemplateMethod()
{
    PrimitiveOperation1();
    PrimitiveOperation2();
}

复制代码

   由上面的例子可以看到,模板方法是关于基类如何调用派生类内操作函数的,是一种反向控制结构,常用于代码复用。

  这种反向结构也体现了一个设计原则,即好莱坞原则 — “不要给我们打电话,我们会打给你”

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值