Effective C++ 5.实现

发布时间:2019-06-22 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了Effective C++ 5.实现脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

实现

条款26: 尽可能延后变量定义式的出现时间

“尽可能延后”的真正意义:
1)不只应该延后变量的定义,直到非得使用该变量的前一刻为止,甚至 2)应尝试延后定义直到能提供初值实参为止
第二点的原因:通过默认构造函数构造出一个对象然后对它赋值”比“直接在构造函数时制定初值”效率差

//方法1,1个构造函数+1个析构函数+n个赋值操作;
Widget w;
for (int i = 0; i < n; ++i) 
{ 
  w = some value dependent on i;
  ...
}

//方法2,n个构造函数+n个析构函数
for (int i = 0; i < n; ++i) 
{
     Widget w(some value dependent on i);  
        ...
}     

若方法2的开销小于方法1或对性能要求不高时选择方法2 ,因为方法2满足条款1

条款27: 尽量少做转型动作

static_cast: 用来强迫隐式转换,例如将non-const转型为const,int转型为double等等
const_cast: 通常被用来将对象的常量性转除;即去掉const。
dynamic_cast: 主要用来执行“安全向下转型”,也就是用来决定某对象是否归属继承体系中的某个类型。
reinterPRet_cast: 意图执行低级转型,实际动作可能取决于编译器,这也就表示它不可移植。

class Window{
    public:
        virtual void resize(int s){ size = 0;}
    private:
        int size;
};
class SpecialWindow : public Window{
    public:
        virtual void resize(int s){
            static_cast<Window>(*this).resize(s);
        }
};

注意 static_cast<Window>(*this).resize(s):
在当前对象的副本上调用resize,所以原对象根本没有变化!!!

正确的方式:

class SpecialWindow : public Window{
    public:
        virtual void resize(int s){
           Window::resize(s);
        }
};

使用dynamic_cast,通常是因为你想在一个你认定为derived class对象身上执行 derived class函数操作,但你只有一个base指针或引用。
可以使用1)虚函数或 2) 直接修改函数接口,避免使用base指针或引用

条款28: 避免返回handles指向对象内部成员

问题: 破坏了封装性,可能会导致悬空指针或引用或迭代器

条款29: 为“异常安全”而努力是值得的

当异常被抛出时,带有异常安全的函数:
1. 不泄露任何资 (leakage)
2. 不允许数据败坏 (integrity)

Problem

class PrettyMenu {
public:
    ...
    void changeBackground(std::istream&amp; imgSrc); // change background  image
    ...
private:
    Mutex mutex;            // mutex for this object
    Image *bgImage;         // current background image
    int imageChanges;       // # of times image has been changed
};

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
}

如上代码没有满足异常安全的两点要求:
1) bgImage = new Image(imgSrc)异常返回时,lock没有释放,可能会导致死锁。
2) bgImage = new Image(imgSrc)异常返回时, imageChanges已经增加,但实际并没有变化,因为没有new成功,破坏integrity

解决第一点利用RaiI管理资源


void PrettyMenu::changeBackground(std::istream& imgSrc)
{
    Lock m(&mutex); // Lock 利用shared_ptr管理锁
    delete bgImage;
    ++imageChanges;
    bgImage = new Image(imgSrc);
}

解决第二点:利用copy and swap
先创建一个修改好的对象,成功后,再和待修改对象交换, 确保对象修改的完整性(修改完或不修改)

struct PMImpl { // item31 
    std::tr1::shared_ptr<Image> bgImage;  
    int imageChanges;  
};
class PrettyMenu {
    ...
private:
    Mutex mutex;
    std::tr1::shared_ptr<PMImpl> pImpl;
};
void PrettyMenu::changeBackground(std::istream& imgSrc)
{
    using std::swap;  
    Lock ml(&mutex); 
    
    std::tr1::shared_ptr<PMImpl>                       
    
    pNew(new PMImpl(*pImpl));                           // copy  
    pNew->bgImage.reset(new Image(imgSrc));  
    ++pNew->imageChanges;
    
    swap(pImpl, pNew);                                 // swap 
}

异常安全函数提供三个保证之一:
1. 函数提供基本保证
2. 函数提供强力保证
3. 函数提供不抛出保证: int DOSomething() throw();

函数提供的“异常安全保证”通常最高只等于其所调用之各个函数的“异常安全保证”中的最弱者

条款30: 透彻了解inlining的里里外外

inline的使用
1) inline不要在函数声明时使用,在函数定义时使用 (用户无须知道一个函数是不是inline的)
2) 将函数定义于class内时,相当于在函数前隐式的加了inline
3) inline 具有@R_619_2512@的性质,即当inline函数A声明在B.h,实现在C.cpp时,模块D调用A函数时,编译D没问题,但链接时将出现错误
4) 使用inline只是程序员的一种建议,编译器根据实际需要,判断是否将函数展开

inline优势:
编译期间利用宏的方式,将函数调用展开,减少函数调用开销。

inline劣势:
1)过度使用inline导致代码膨胀,继而可能导致cache命中率降低,影响性能
2)导致模块之间的耦合加剧,因为inline函数的修改一定导致调用该函数的客户代码重新编译。non-inline函数可能只需要重新链接就行了
3)大部分调试器不能针对inline函数进行调试

建议:
0)开始时先不要将任何函数声明为inline,或至少将inlining施行范围局限在那些“一定成为inline”或“十分平淡无奇”的函数身上
1) 将大多数inlining限制在小型、被频繁调用的函数身上。这可使日后的调试过程和二进制升级更容易,也可使潜在的代码膨胀问题最小化
2) 不要只因为function templates出现在头文件,就将它们声明为inline

条款31:将文件间的编译依存关系降至最低

问题
对一个头文件中的class类中的成员(即便是private成员)进行修改,导致其他调用该头文件的模块重新编译(编译依赖)

// #include "Person.h" 头文件
#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.h”或"Address.h的任意修改都会导致调用Person.h的客户程序的重新编译

Solution 1 : 使用 pimpl idiom机制
头文件定义一个pimpl的指针,将Person部分成员和函数的实现交由 pImpl指向的类实现

// #include "Person.h" 头文件
#include <string> 
class PersonImpl; // 前置声明
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: // ptr to implementation;
    std::tr1::shared_ptr<PersonImpl> pImpl;·--// see Item 13 for info on
}; 
// #include "PersonImpl.h" 头文件
#include <string>
#include "Date.h"
#include "Address.h"

struct PersonImpl{
    std::string theName; // implementation detail
    Date theBirthDate; // implementation detail
    Address theAddress; // implementation detail
    std::string name() const { return theName; }
    std::string birthDate() const { return theBirthDate.birthDate(); }
    std::string address() const { return theAddress.address(); }
};
// #include "Person.cpp" 

#include "Person.h"
#include "PersonImp.h"

std::string name() const{
    return pImpl->name();
}
std::string birthDate() const{
    return pImpl->birthDate();
}
std::string address() const {
     return pImpl->address();
}

Solution 2 利用抽象基类

// #include "Person.h" 头文件 抽象基类
#include <string>
#include <memory>
class Person{
    public:
        virtual ~Person(){}                     // 书本这个地方出错
        virtual std::string getName() const = 0;
        virtual unsigned int getAge() const = 0;
        virtual char getSex() const = 0 ;
        // 这个static函数是关键
        static boost::shared_ptr<Person> create(const std::string& name, unsigned int age, char sex);
};
//#include "RealPerson.cpp" 对抽象基类的实现
#include "Person.h"
#include <memory>

class RealPerson : public Person
{
public:
    RealPerson(const std::string& _name, unsigned int _age, char _sex): name(_name), age(_age),sex(_sex) {}
    ~RealPerson() {}
    std::string getName() const { return name; }
    unsigned int getAge() const { return age;  }
    char getSex() const { return sex; }
private:
    std::string name;
    unsigned int age;
    char sex;
};

boost::shared_ptr<Person> Person::create(const std::string& _name, unsigned int _age, char _sex)
{
    return boost::shared_ptr<Person>(new RealPerson(_name, _age, _sex));
}
// 客户代码
#include "Person.h" 
int main(){
    boost::shared_ptr<Person> person = Person::create("ShiYang",27, 'F');
    ...
    return 0;
}

脚本宝典总结

以上是脚本宝典为你收集整理的Effective C++ 5.实现全部内容,希望文章能够帮你解决Effective C++ 5.实现所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。