细读《Effective C++》之六

Chapter 5. Implementations

Item 29 - 31

条款29:Strive for exception-safe code
void  PrettyMenu::changeBackground(std::istream &  imgSrc)
{
  
lock(&mutex);                      // acquire mutex (as in Item 14)
  delete bgImage;                    // get rid of old background
  ++imageChanges;                    // update image change count
  bgImage = new Image(imgSrc);       // install new background
  unlock(&mutex);                    // release mutex
}

Exception safety的两个条件:

1) Leak no resources:new失败将导致unlock不会执行,mutex就不会被释放。解决之道:借助13、14款的思想,用资源管理类实现对mutex和bgImage的管理。

2) Don't allow data structures to become corrupted:new失败,则bgImage指向已删除对象,imageChanges已累加。异常的主要特点是它的不确定性,这也正是其危险性所在。

Scott给出exception-safe functions可以提供的三个保证:

1) Basic guarantee:用适当的方法代替异常。we could write changeBackground so that if an exception were thrown, the PrettyMenu object might continue to have the old background image, or it might have some default background image, but clients wouldn't be able to predict which.

2) strong guarantee:如果失败则回到调用前状态。通过copy and swap可以实现,实现原则:Make a copy of the object you want to modify, then make all needed changes to the copy. If any of the modifying operations throws an exception, the original object remains unchanged. After all the changes have been successfully completed, swap the modified object with the original in a non-throwing operation. 这种手法被称为pimpl idiom,思想有点类似于双缓冲绘图:

struct  PMImpl  {                               // PMImpl = "PrettyMenu
  std::tr1::shared_ptr<Image> bgImage;        // Impl."; see below for
  int imageChanges;                           // why it's a struct
}
;

class  PrettyMenu  {
  ...
private:
  Mutex mutex;
  std::tr1::shared_ptr
<PMImpl> pImpl;
}
;

void  PrettyMenu::changeBackground(std::istream &  imgSrc)
{
  
using std::swap;                            // see Item 25, partially specialize
  Lock ml(&mutex);                            // acquire the mutex
  std::tr1::shared_ptr<PMImpl>                // copy obj. data
    pNew(new PMImpl(*pImpl));
  pNew
->bgImage.reset(new Image(imgSrc));     // modify the copy
  ++pNew->imageChanges;
  swap(pImpl, pNew);                          
// swap the new data into place
}
                                              //  release the mutex
3) Nothrow guarantee:内置类型可以提供nothrow保证。
 
条款30:Understand the ins and outs of inlining

inline函数没有the cost of a function call,然而there is no free lunch,inline会造成程序体积增加,可能导致额外换页,降低cahce命中率,从而造成效率降低。另外,inline is a request to compilers, not a command. The request can be given implicitly or explicitly. 那些contain loops or are recursive的函数不会被inline,virtual functions也不会被inline,通过函数指针调用的函数也不会被inline,最后,因为有支“看不见的手”在摆布缺省constructors和destructors,这将影响它们的inline。

如果一个函数被compiler以inline实现,该函数一旦被修改,所有使用它的文件都要重新编译。

80-20 rule: a typical program spends 80% of its time executing only 20% of its code.

条款31:Minimize compilation dependencies between files

我在大多数时间写下这样的代码:

#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;        // implementation detail
      Date theBirthDate;          // implementation detail
      Address theAddress;         // implementation detail
}
;

当Date或Address甚至它们所包含的头文件中有任何改变,不仅它们要被重新编译,就连含person的所有头文件也要重新编译。使用前置声明来解决这个问题:

#include  < string >                //  standard library components shouldn't be forward-declared
class  Date;                     //  forward declaration
class  Address;                  //  forward declaration

class  Person  {
……
}
;

GoF倡导的两大原则之一:针对接口编程,而不是针对实现编程。将实现独立出来,就是pimpl(pointer to implementation) idiom:

#include  < string >                        //  standard library components shouldn't be forward-declared
#include  < memory >                        //  for tr1::shared_ptr; see below

class  PersonImpl;                       //  forward decl of Person impl. class
class  Date;                             //  forward decls of classes used in
class  Address;                          //  Person interface

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:                                   // ptr to implementation;
  std::tr1::shared_ptr<PersonImpl> pImpl;  // see Item 13 for info on
}
;                                          //  std::tr1::shared_ptr

1) Avoid using objects when object references and pointers will do.

2) Depend on class declarations instead of class definitions whenever you can.

3) Provide separate header files for declarations and definitions.

用“声明的依存性”代替“定义的依存性”是编译依存性最小化的本质,上述方法中,Person被称为Handle class。Handle classes的成员函数通过pimpl取得数据,增加了一次间接访问,而且pimpl也必须在Handle classes的constructors中初始化。

将实现与接口独立,GoF给出了创建类的一个模式Factory Method,被Scott称为Interface class,这样的抽象类只有virtual destructor和pure virtual functions作为接口。Interface classes也有一次indirect jump cost和一个vptr。

看完这一章之后,我的一个感觉就是:作为一个程序员,你可以很快地学会一门语言的语法,并写出可以顺利通过编译的程序。但要写出真正优质的代码,不仅仅要掌握语法,还要掌握如何借语言完美地表达需求。唯一的出路在于忘记语法,我现在仅仅是能够理解语法,理解地很僵硬、很教条,还不能忘记语法。

有时想写点感想,觉得会相当力不从心,还是没有深刻体会吧。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PHP 和 JavaScript 都支持 JSON 数据格式,但是在处理 JSON 数据时有一些不同之处。 在 PHP 中,可以使用 `json_encode()` 函数将数组或对象转换为 JSON 格式的字符串,例如: ```php $data = array('name' => 'John', 'age' => 30); $json = json_encode($data); echo $json; // 输出 {"name":"John","age":30} ``` 同样地,在 PHP 中,可以使用 `json_decode()` 函数将 JSON 格式的字符串转换为数组或对象,例如: ```php $json = '{"name":"John","age":30}'; $data = json_decode($json); echo $data->name; // 输出 John ``` 在 JavaScript 中,可以使用 `JSON.stringify()` 方法将对象转换为 JSON 格式的字符串,例如: ```javascript var data = {name: 'John', age: 30}; var json = JSON.stringify(data); console.log(json); // 输出 {"name":"John","age":30} ``` 同样地,在 JavaScript 中,可以使用 `JSON.parse()` 方法将 JSON 格式的字符串转换为对象,例如: ```javascript var json = '{"name":"John","age":30}'; var data = JSON.parse(json); console.log(data.name); // 输出 John ``` 需要注意的是,在 PHP 中,可以将 JSON 格式的数据直接解码为关联数组或对象,而在 JavaScript 中,解析 JSON 数据时始终会得到一个对象。如果要得到关联数组,则需要手动处理。例如,在 JavaScript 中,可以将解析后的对象转换成关联数组: ```javascript var json = '{"name":"John","age":30}'; var data = JSON.parse(json); var dataArray = []; for (var key in data) { dataArray.push({key: key, value: data[key]}); } console.log(dataArray); // 输出 [{key:"name",value:"John"},{key:"age",value:30}] ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值