实现

1.尽量少做转型动作

旧式转型:
    Effective C++学习笔记(六)
C++新式转型:
    
Effective C++学习笔记(六)
 
Effective C++学习笔记(六)
 
2.避免返回handles指向对象的内部成分

引用、指针和迭代器统统都是所谓的handles(号码牌,用来取得某个对象),而返回一个“代表对象内部数据”的handles,随之而来的是“降低对象封装性”的风险,同时,它可能导致“虽然调用const成员函数却造成对象状态被改变”。

class Point{
public:
    Point(int x,int y);
    ...
    void setX(int newval);
    void setY(int newval);
    ...
};

struct RectData{
    Point ulhc;
    Point lrhc;
};

class Rectangle{
public:
    ...
    const Point& upperLeft() const{return pData->ulhc;} //在返回类型上加const就可以轻松去除下面将要提到的两个问题
    const Point& lowerRight() const{return pData->lrhc; ...};
 
//调用
Point c1(0,0);
Point c2(100,100);
const Rectangle rec(c1,c2);
rec.upperLeft().setX(50);//错误,客户只能读取Point,不能改写它

1、成员变量的封装性最多只能等于“返回引用”的函数的访问级别。在本例中,public成员函数返回类中的私有成员变量,让私有成员的访问权限等同于公有的 。
2、const成员函数传出来一个引用,后者所指的数据与对象自身有关联,而它又被存储于对象之外,那么这个函数的调用者可以修改那笔数据。

所谓虚吊号码牌:常见的来源就是函数返回值,函数返回的指针或引用是函数体内部的临时变量,在函数体结束时,临时变量会被析构,最终导致指针或引用指向一个不再存在的对象。
Effective C++学习笔记(六)
 
3.为“异常安全”而努力是值得的

有异常抛出时,带有异常安全性的函数应该:
  • 不泄露任何资源
  • 不败坏任何数据

异常函数的三个保证:基本型、强烈性和不抛异常型
  • 基本型:如果异常被抛出,程序内的任何事物仍然保持在有效状态下。没有任何对象或数据结构会因此而败坏,所有对象都处于一种内部前后一致的状态。
  • 强烈型:如果异常被抛出,程序状态不变。如果函数成功,就是完全成功,若果函数失败,程序会回到“调用函数之前”的状态。
  • 不抛掷保证:承诺绝不抛出异常,因为他们总是能够完成他们原先承诺的功能。作用于内置类型身上的所有操作都提供不抛掷异常的保证。这是异常安全码中必不可少的关键基础材料。

强烈保证往往可以通过 copy-and-swap 技术实现

Effective C++学习笔记(六)
 

4.将文件间的编译依存关系降至最低

先看一下一个例子:

#include<string>
#include"date.h"
#include"address.h"
class Person {
public:
    Person(const std::string& name, const Date& birthday, const Address& addr);
    std::string name() const;
    std::string birthDate() const;
    std::string address() const;
    ...
private:
    std::string theName;  //实现细目
    Date theBirthDate;
    Address theAddress;
  };

上述三个头文件和Person之间的编译存在依赖关系,三个头文件中任何一个有所改变,所有包含Person class的文件就得重新编译,为了解决上述问题,我们将考虑handle class。

(1)采用pimpl idiom设计
    pimpl为pointer to implementation,这种class内的指针名通常为pImpl。

// Person 接口
  #include <string>
  #include <memory>

  // 前置声明
  class PersonImpl;   // Handle classes
  class Date;
  class Address;

  class Person {
  public:
      Person(const std::string& name, const Date& birthday, const Address& addr);
      std::string name() const;
      std::string birthDate() const;
      std::string address() const;
      //...
  private:
      // pimpl idiom(pointer to implementation)
      std::tr1::shared_ptr<PersonImpl> pImpl;    // 指针,指向实现物,shared_ptr 在memory头文件
  };


// Person 的Handle class,实现Person class中的接口
#include "Person.h"   // Person的定义式
// include PersonImpl的class定义式,用于调用其成员函数,
// 注意PersonImpl有着和Person完全相同的成员函数。两者接口完全相同

#include "PersonImpl.h" 

Person::Person(const std::string& name, const Date& birthday, const Address& addr)
       :pImpl(new PersonImpl(name, birthday, addr))
       {}
std::string Person::name() const
{
   return  pImpl->name();            //让Person变成一个Handle class 并不会改变它做的事,智慧改变它做事的方法
}

(2)采用 Interface class,令Person class 为 abstract base class

class Person {
  public:
      virtual ~Person();
      virtual std::string name() const = 0;
      virtual std::string birthDate() const = 0;
      virtual std::string address() const = 0;
      ...
  };

对于 Interface class用户通常通过“factory函数或virtual构造函数”实现具体化,该函数往往被声明为static:

class Person {
    ...
    static std::tr1::shared_ptr<Person>
    create(const std::string& name, const Date& birthday, const Address& addr);
};

Derived class:

class RealPerson: public Person {
 public:
      RealPerson(const std::string& name, const Date& birthday, const Address& addr)
          :theName(name), theBirthDate(birthday), theAddress(addr)
      {}
      virtual ~RealPerson() {}
      std::string name() const;
      std::string birthDate() const;
      std::string address() const;
      //...
 private:
      /*实现条目*/
      std::string theName;
      Date theBirthDate;
      Address theAddress;

 };

std::tr1::shared_ptr<Person> Person::create(const std::string& name,
     const Date& birthday,
     const Address& addr)
{
    return std::tr1::shared_ptr<Person>(new RealPerson(name, birthday, addr));
}

编译依赖性最小化的本质:
  • 如果使用object reference或object pointers可以完成任务,就不要使用objects。
  • 如果能够,尽量以class声明式替换class定义式。
  • 为声明式和定义式提供不同的头文件。

相关文章: