Welcome 微信登录
编程资源 图片资源库 蚂蚁家优选 PDF转换器

首页 / 操作系统 / Linux / 从引用传递到设计模式

1  值传递值传递实际上是,拷贝实参的值传给形参,常用于“小对象” (small objects)int fact(int val) // factorial of val
{
    int ret = 1;
   
    // assign ret * val to ret and decrement val
    while (val > 1)
        ret *= val--;
   
    return ret;
}调用该函数:cout << "5! is " << fact(5) << endl;<Effective C++> 中提及,值传递适用的“小对象”为:内置类型(built-in types),STL迭代器,函数对象类型(function object types)<C++ Programming Lauguage> 中有一个小例子, 只包含一对 int 型数据成员(x 和 y) 的 Point 类,也可视为小对象void Point::operator+=(Point delta); // pass-by-value2  引用传递引用传递不涉及拷贝,传递给形参的是实参变量的引用,常用来传递“大数值” (large values)使用引用传递,有两个优点:更高效,防切断2.1  更高效下面是 Person 基类,及其派生类 Student 的定义// class Person and its subclass Student
class Person{
public:
    Person();
    virtual ~Person();
private:
    std::string name;
    std::string address;
};class Student: public Person{
public:
    Student();
    ~Student();
private:
    std::string schoolName;
    std::string schoolAddress;
};现有一个验证学生身份的函数,形参为值传递,则拷贝实参给形参的代价是:调用基类 Person 的构造函数一次,基类内 string 型数据成员的构造函数两次,派生类 Student 的构造函数一次,派生类内 string 型数据成员两次,最后还会调用相应的析构函数六次,共计十二次调用,自然效率低下。而使用引用传递,并不涉及拷贝操作,故而显著的提高了效率。// 1) pass-by-value
bool validateStudent(Student s);// 2) pass-by-reference-to-const
bool validateStudent(const Student& s);2.2  防切断下面的例子中,派生类 WindowWithScrollBars 中,重写了基类 Window 的虚函数 display// base class Window
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()// pass-by-value is incorrect
void printNameAndDisplay(Window w)
{
    std::cout << w.name();
    w.display();
}// WindowWithScrollBars object will be sliced off
WindowWithScrollBars  wwsb;
printNameAndDisplay(wwsb);因为在 printNameAndDisplay 中,并不修改传递进来的参数。因此,可使用 pass-by-const-reference 的形式,避免“切断”的发生3  动态绑定上面"切断"的例子,实际上涉及的是 C++ 的动态绑定机制 (dynamic binding), 而动态绑定的一个关键就是引用传递,下面看个更形象的例子:class Quote {
public:
    std::string isbn() const;
    virtual double net_price(std::size_t n) const;
};// Bulk_quote inherits from Quote
class Bulk_quote : public Quote {
public:
    double net_price(std::size_t) const override;
};在 cal_total 函数中,需要调用 net_price,采用 pass-by-const-reference 形式// calculate the price
double cal_total(const Quote &item, size_t n)
{
    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); // calls Quote::net_price
cal_total(bulk, 20); // calls Bulk_quote::net_priceC++ 的动态绑定也叫“迟邦定”,它使程序直到运行时,才基于引用或指针绑定的对象类型,来选择调用哪个虚函数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 和 PrimitiveOperation24.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();
}由上面的例子可以看到,模板方法是关于基类如何调用派生类内操作函数的,是一种反向控制结构,常用于代码复用。这种反向结构也体现了一个设计原则,即好莱坞原则 — “不要给我们打电话,我们会打给你”小结:1) use pass-by-value for small objects;use pass-by-const-reference to pass large values that you don’t need to modify2) dynamic binding happens when a virtual function is called through a reference (or a pointer) to a base class3) Tempalte method pattern - define the skeleton of an algorithm in an operation, deferring some steps to subclasses.4) Hollywood principle - don"t call us, we"ll call you参考资料: <C++ Programming Language_4th> ch 12.2.1 <Effective C++_3rd> item 20 <C++ Primer_5th> ch 6.2,  ch 15.1 <Design Patterns>更多详情见请继续阅读下一页的精彩内容: http://www.linuxidc.com/Linux/2016-04/130795p2.htm