Effective C++ 笔记(2)

1.       继承与面向对象设计

条款32: 确定你的public继承塑模出is-a关系

条款33: 避免遮掩继承而来的名称

无论是virtualnon-virtual, 如果derived class有复写,则屏蔽了virtual函数.编译器首先寻找derived作用域中函数名,再寻找base,如果找到则停止,并不判断参数.

 class base

{

public:

         virtual void mf1();

         virtual void mf1(int);

         virtual void mf2();

         void mf3();

         void mf3(int);

};

class derived: public base

{

public:

         virtual void mf1();

         void mf3();

         void mf4();

}

derived d;

d.mf1(); //ok, derived::mf1;

d.mf1(1); //error, derive::mf1() cover base::mf1()

d.mf2(); //ok, base::mf2()

d.mf3(); //ok, derived::mf3();

d.mf3(1); //error, derive::mf3() cover base::mf3()

 

derived d;

base *p = &d;

p->mf1(); // derived, base::mf1;

p->mf3();//ok, base::mf3;

这违背is-a关系,可以使用using声明

class derived: public base

{

public:

         using base::mf1; /*base中名为mf1,mf3的所有东西在derived作用域中可见*/

         using base::mf3;

         virtual void mf1();

         void mf3();

         void mf4();

};


条款34: 区分接口继承和实现继承

 pure virtual: 只继承接口

simple virtual: 继承接口和缺省实现

non-virtual: 如果在derived class中重写则覆盖,所以不应覆写,代表的是不变性和凌驾特异性,继承接口和一份强制实现

class airport{};

class airplane

{

public:

         virtual void fly(const airport &);

};

class airplaneA: public airplan

{

//未定义fly函数,继承airplan::fly

};

class airplaneB: public airplan

{

//未定义fly函数,继承airplan::fly

};

如果derived有共同行为,可以将函数共同行为定义在base class中,随之而来的问题是,如果有增加一个derived class行为不同且未重定义函数,则造成错误.

class airplaneC: public airplan

{

//未定义fly函数,继承airplan::fly,但是airplaneC fly方式不同,出错!!

};

所以需要将接口和缺省分开.

class airplane

{

public:

         virtual void fly(const airport &) = 0;

protected:

         void defaultfly(const airport &);

};

class airplanA: public airplan

{

         virtual void fly(const airport &destination)

         {

                   defaultfly(destination);

         }

};

class airplanB: public airplan

{

                   virtual void fly(const airport &destination)

         {

                   defaultfly(destination);

         }

};

class airplanC: public airplan

{

                   virtual void fly(const airport &destination)

         {

                   //C own fly mode

         }

};

或者(个人喜欢后者)

class airplane

{

public:

         virtual void fly(const airport &) = 0;

 

};

void airplane::fly(const airport &destination)

{

         //default fly mode

}

class airplanA: public airplan

{

         virtual void fly(const airport &destination)

         {

                   airplane::fly(destination);

         }

};

class airplanB: public airplan

{

         virtual void fly(const airport &destination)

         {

                   airplane::fly(destination);

         }

};

class airplanC: public airplan

{

         virtual void fly(const airport &destination)

         {

                   //C own fly mode

         }

};

 

条款35: 考虑virtual函数以外的其他选择

NVI手法,non-virtual interface,这个思想流派主张virtual函数应该几乎总是private.而创建另一个non-virtual函数调用virtual函数,做一些事前工作或时候工作.

class airplane

{

public:

         int fly()

         {

                   //...

                   dofly();

                   //...

         }

private:

         virtual void dofly(const airport &) ;

}

另一种strategy

class airplane;

int defaultfly();

 class airplane

{

public:

         typedef void (*pflymodefunc)();

         explicit airplane(pflymodefunc ff = defaultfly) :flyfunc(ff)

         {

         }

         void fly() const

         {

                   flyfunc();

         }

private:

         pflymodefunc flyfunc ;

};

class myairplane: public airplane

{

         explicit airplaneA(pflymodefunc ff = defaultfly)

                   : airplane (ff)

         {

         }

 

};

void airplaneA_fly(void);

void airplaneB_fly(void);

myairplane planeA(airplaneA_fly); //不同的飞行方式

myairplane planeB(airplaneB_fly); //不同的飞行方式

借由tr1::function完成strategy模式

class airplaneC_fly

{

         void operator ()(void) const

         {

         }

};

class airplane

{

public:

         typedef std::tr1::function<void ()> pflymodefunc;

/*void ()代表的是无参数,返回值为void的函数, tr1::function类型产生的对象可以持有任何遇刺签名式兼容的可调用物, 所为兼容,是指调用的变量可以隐式转换为参数类型,而实际返回值可以转换为返回值类型*/

         explicit airplane(pflymodefunc ff = defaultfly)

                   :flyfunc(ff)

         {

         }

         void fly() const

         {

                   flyfunc();

         }

private:

         pflymodefunc flyfunc ;

};

void airplaneA_fly(void);

myairplane planeA(airplaneA_fly); //不同的飞行方式

myairplane planeC(airplaneC_fly()); //不同的飞行方式,函数对象

 

古典的strategy模式

将继承体系内的virtual函数替换为另一个继承体系内的virtual函数.

class flymode

{

         virtual void fly()

         {

         }

};

flymode defaultflymode;

class airplane

{

public:

         typedef std::tr1::function<void ()> pflymodefunc;

 

         explicit airplane(flymode ff = defaultfly)

                   :pflymode(ff)

         {

         }

         void fly() const

         {

                   pflymode->fly();

         }

private:

         flymode pflymode ;

};

 

条款36: 绝不重新定义继承而来的non-virtual函数

对于动态绑定, 如果是virtual函数, 则现在derived class中寻找,如果没有寻到, 则在base class中寻找, 如果是non-virtual,则直接在base class中寻找.

class base

{

public:

         virtual void func1();

         void func2();

}

class derived: public base

{

public:

         virtual void func1();

         void func2();

}

base *p = new derived();

p->func1(); //derived::func1()

p->func2(); //base::func2()

delete p;

derived d;

d->func2(); //derived::func2()

 

条款37: 绝不重新定义继承而来的缺省函数值

virtual系数是动态绑定,而缺省函数值却是静态绑定.

class base

{

public:

         virtual void func1(int i = 0);

         virtual void func2(int i = 0);

};

class derived: public base

{

public:

         virtual void func1(int i = 1);

};

class derived2: public base

{

public:

         virtual void func2(int i);

};

base *p = new derived();

p->func1(); //derived::func1(0) , 动态绑定时, 默认参数从base继承

delete p;


 *p = new derived2();

p->func2(); //base::func2(0) , 动态绑定时, 默认参数从base继承

delete p;

 

derived2 d2;

d2->func2(1); //静态绑定时, 不继承默认实参

d2.func2(); /* error, 'derived::func2' : function does not take 0 arguments*/

 

条款38: 通过复合塑模出has-a或根据某物实现出

条款39: 明智而谨慎地使用private继承

如果classes之间的继承关系是private, 编译器不会自动将一个derived class对象转换为一个base classs对象. private继承只是一种实现技术,private继承意味只有实现部分被继承,接口部分被略去.

class base{};

class derived: private base{};

void func(const base&); //或者void func(const base)

derived a;

func(a); /* error, 'type cast' : conversion from 'derived *__w64 ' to 'const base &' exists, but is inaccessible*/

 

private继承类似has-a, base的某些接口不想让用户使用或误用, 但需要重写某些virtual函数,使用private继承.

class Timer

{

public:

         virtual void onTick(int i = 0); //call it once timer stricked

};

class myTimer: private Timer

{

private

virtual void onTick(int i = 0);

};

 

可用复合代替

class Timer

{

public:

         virtual void onTick(int i = 0); //call it once timer stricked

};

class myClass

{

private:

         class myTimer: public Timer

         {

         public:

                   virtual void func2(int i);

         }

         derived d;

};

 

如果class无成员变量, virtual function(vptr),无virtual base classed(这样的base class也会带来体积上的额外开销), 这样的empty class不使用任何空间, 但是C++判定,独立对象必须有非零大小.

class empty

{

};

classs derived{

private:

         int x;

         empty e:

}

sizeof (derived) > sizeof(int);

sizeof(empty) == 1 /*或者更大(齐位要求)*/

C++会默默安插一个char到空对象内,但是这个约束不适合derived class对象内的base clas成分.

classs empty_derived: private empty

{

private:

         int x;

}

sizeof(empty_derived) == sizeof(int); /*EBO(empty base optimization; 空白基类最优化, 一般只在单一继承下才可行*/

 

条款40: 明智而谨慎地使用多重继承

 virtural继承的classs所产生的对象比使用non-virtual继承的class体积大,且访问成员的速度慢, 而且virtual base class的初始化由最底层derived class负责,如果非必要, 不要使用virtual继承.

但是多重继承涉及 public继承某个interface class(pure virtual class), private继承某个写组实现的class的组合, 则正当.

 

7. 模板与泛型编程

 

条款41: 了解隐式接口和编译期多态

条款42: 了解typename的双重意义.

typename比较class多了一个意义: 声明是嵌套从属名称.

template<typename T>void func(ITerT iter)

{

         C::const_iterator *x; /*编译期认为C::const_iterator不是类型而是变量, 如果C::const_iterator是一个变量,*代表乘号.*/

typename C::const_iterator *x; //ok

typename  std::iterator_traits<ITerT>::value_type temp(iter); /*代表对象所指物的类型,如果ITerTvector<int>iterator,temp就是int*/

}

template不可再base classes list,也不可再member initialization list中作为base class 修饰符.

 

条款43: 学习处理模板化基类内的名称

template<typename T> class base

{

         void func();

}

template<typename T> class derived: public base<T>

{

         void func2()

         {

                   func(); /*不能通过编译,有可能基类被特例化而无func函数,所以func对编译器不可见.*/

         }

}

template<c> class base<classtype>

{

//func函数.

}

需要在derived中使用using声明

template<typename T> class derived: public base<T>

{

using base<T>::func;

         void func2()

         {

                   func(); //不能通过编译,有可能基类被特例化而无func函数.

         }

}

或者

template<typename T> class derived: public base<T>

{

         void func2()

         {

                   this->func(); //不能通过 ?,有可能基类 被特例化而无函数.

         }

}

 

derived<classtype> d;

d.func2(); //编译错误.

 

条款44: 将与参数无关的代码抽离templates

条款45: 运用成员函数模板接受所有兼容类型

智能指针

template<typename T>class smrtptr

{

public:

template<typename U>smartptr(const SmartPtr<U> &other): ptr(other.get())

         {

         }

/*编译器会自动生成copy构造函数smartptr(smart const&)*/

         explicit smartptr<T *other>;

         //...

         T *get() const

         {

                   return ptr;

         }

private:

         T *ptr;

};

smartptr<base> ptr = smart<derived>(new derived);

 

条款46: 需要类型转换时请为模板定义非成员函数

template<typename T>class myclass

{

public:

         myclass(const T &n = 0, const T &d = 1);

};

template<typename T> const myclass<T> operator *(const myclass<T> &lhs, const myclass<T> &rhs)

{

}

myclass<int> onehalf(1, 2);

myclass<int>result = onehalf * 2; /* error, 'const myclass <T> operator *(const empty<T> &,const empty<T> &)' : could not deduce template argument for 'const empty<T> &' from 'int' , template无法通过构造函数隐式进行隐式类型转化, 所以无法将2转换为myclass<int>从而将T推断为int*/

 

template<typename T>class empty

{

public:

         empty(const T &n = 0, const T &d = 1){}

         friend

         const empty operator *(const empty &lhs, const empty &rhs); /*被声明却没被定义.*/

};

template<typename T> const empty<T> operator *(const empty<T> &lhs, const empty<T> &rhs)

{

}

empty <int> onehalf(1, 2);

empty <int>result = onehalf * 2; /*无法link, error LNK2028: unresolved token (0A00029C) "class empty<int> const __cdecl operator*(class empty<int> const &,class empty<int> const &)" (??D@$$FYA?BV?$empty@H@@ABV0@0@Z) referenced in function "int __cdecl main(int,char * * const)" (?main@@$$HYAHHQAPAD@Z)*/

由于onehalf被声明为empty<int>class empty<T>被具化, friend函数operator *也被自动声明, 所以编译其可以使用构造函数进行隐式转换.

 

template<typename T>class empty

{

public:

         empty(const T &n = 0, const T &d = 1){}

         friend

         const empty operator *(const empty &lhs, const empty &rhs)

{

}

};

empty <int> onehalf(1, 2);

empty <int>result = onehalf * 2; //ok

 

条款47: 请使用traits classed表现类型信息

条款48: 认识template元编程

 

8 定制newdelete

条款49: 了解new-handler的行为

operator new抛出异常以反映一个未获满足的内存需求之前, 它会先调用一个客户指定的错误处理函数, 一个所谓的new-handler, 调用set_new_handler函数, 声明于<new>的一个标准程序库函数.

namespace std

{

         typedef void (*new_handler)();

         new_handler set_new_handler(new_handler p) throw(); /*返回旧的handler*/

}

class专属new-handler

class myclass

{

public:

         static std::new_handler set_new_handler(std::new_handler p) throw();

         static void* operator new(std::size_t size) throw(std::bad_alloc);

/*调用set_new_handler, call global operator new, 分配失败,则会调用new_handler, 如果最终无法分配足够内存, 抛出bad_alloc, 此情况下, 必须确保原本的new_handler恢复, 如果分配成功, 返回指针, 析构函数需要将原本的handler恢复*/

private:

         static std::new_handler currenthandler;

};

static std::new_handler myclass ::currenthandler = NULL;

std::new_handler myclass::set_new_handler(std::new_handler p) throw()

{

         std::new_handler oldhandler = currenthandler;

         currenthandler = p;

         return oldhandler;

}

void* myclass::operator new(std::size_t size) throw(std::bad_alloc)

{

         newhandlerholder(std::set_new_handler(currenthandler));

         return std::operator new(size);

}

 

下列class, newhandlersupport, 虽然没有用到T, 但是使用template是为了让每一个拥有异体的newhandersupport(更确切地说是currenthandler)

template<typename T>

class newhandlerholder

{

public:

         explicit newhandlerholder(std::new_handler):handler(nh){}

         ~newhandlerholder(){std::set_new_handler(handler)}

 

private:

         std::new_handler handler;

         newhandlerholder(const newhandlerholder&);

         newhandlerholder& operator=(newhandlerholder &);

};

template<typename T>

class newhandlersuport

{

public:

         static std::new_handler set_new_handler(std::new_handler p) throw();

         void* operator new(std::size_t size) throw(std::bad_alloc)

         {

                   newhandlerholder(std::set_new_handler(currenthandler));

                   return std::operator new(size);

         }

private:

         static std::new_handler currenthandler;

};

template<typename T> std::new_handler newhandlersuport<T>::currenthandler = NULL;

template<typename T> std::new_handler newhandlersuport<T>::set_new_handler(std::new_handler p) throw()

{

         std::new_handler oldhandler = currenthandler;

         currenthandler = p;

         return oldhandler;

}

class empty: public newhandlersuport<empty>

{

};

 

9: 杂项讨论

条款53: 不要轻忽编译器的警告

条款54: 让自己熟悉包括TR1在内的标准程序库

条款55: 让自己熟悉Boot

原文地址:https://www.cnblogs.com/zengyou/p/2195595.html