条款32: 确定你的public继承塑模出is-a关系
条款33: 避免遮掩继承而来的名称
无论是virtual或non-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); /*代表对象所指物的类型,如果ITerT是vector<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 定制new和delete
条款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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?