打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
M25 -to - M31(Techniques)
M25:将构造函数和非成员函数虚拟化
虚拟构造函数是指能够根据输入给它的数据的不同而建立不同类型的对象。
虚拟拷贝构造函数能返回一个指针,指向调用该函数的对象的新拷贝。
被派生类重定义的虚拟函数不用必须与基类的虚拟函数具有一样的返回类型。如果函数的返回类型是一个指向基

类的指针(或一个引用),那么派生类的函数可以返回一个指向基类的派生类的指针(或引用)。
虚拟构造函数的实现就是调用虚拟拷贝函数。
虚拟化非成员函数
具有虚拟行为的非成员函数很简单。你编写一个虚拟函数来完成工作,然后再写一个非虚拟函数,它什么也不做

只是调用这个虚拟函数。为了避免这个句法花招引起函数调用开销,你当然可以内联这个非虚拟函数。
M26:限制某个类所能产生的对象数量
如果想为打印机建立类,但是要遵守我们只有一个对象可用的约束,我们应把打印机对象封装在一个函数内,以便让每个人都能访问打印机,但是只有一个打印机对象被建立。
不要建立包含局部静态数据的非成员函数。带private构造函数的类不能作为基类使用,也不能嵌入到其它对象中。

我们可以建立一个具有对象计数功能的基类,以让其他类私有继承这个基类。

使用计数类模板可以自动生成适当数量的计数器,因为我们能让计数器成为从模板中生成的类的静态成员:

template<class BeingCounted>

class Counted {
public:
  class TooManyObjects{};                     // 用来抛出异常 
  static int objectCount() { return numObjects; } 
protected:
  Counted();
  Counted(const Counted& rhs); 
  ~Counted() { --numObjects; } 
private:
  static int numObjects;
  static const size_t maxObjects; 
  void init();                                // 避免构造函数的
};                                            // 代码重复 
template<class BeingCounted>
Counted<BeingCounted>::Counted()
{ init(); } 
template<class BeingCounted>
Counted<BeingCounted>::Counted(const Counted<BeingCounted>&)
{ init(); } 
template<class BeingCounted>
void Counted<BeingCounted>::init()
{
  if (numObjects >= maxObjects) throw TooManyObjects();
  ++numObjects;
}
M27:要求或禁止在堆中产生对象
要求:非堆对象(non-heap object)在定义它的地方被自动构造,在生存时间结束时自动被释放,所以只要禁

止使用隐式的构造函数和析构函数,就可以实现这种限制。最好让析构函数成为private,让构造函数成为

public。你可以引进一个专用的伪析构函数,用来访问真正的析构函数。
判断一个对象是否在堆中
令人伤心的是不仅没有一种可移植的方法来判断对象是否在堆上,而且连能在多数时间正常工作的“准可移植”

的方法也没有。如果你实在非得必须判断一个地址是否在堆上,你必须使用完全不可移植的方法,其实现依赖于

系统调用,只能这样做了。因此你最好重新设计你的软件,以便你可以不需要判断对象是否在堆中。
如果你发现自己实在为对象是否在堆中这个问题所困扰,一个可能的原因是你想知道对象是否能在其上安全调用

delete。这种删除经常采用“delete this”这种声明狼籍的形式。不过知道“是否能安全删除一个指针”与“

只简单地知道一个指针是否指向堆中的事物”不一样,因为不是所有在堆中的事物都能被安全地delete。
幸运的是“判断是否能够删除一个指针”比“判断一个指针指向的事物是否在堆上”要容易。因为对于前者我们

只需要一个operator new返回的地址集合。因为我们能自己编写operator new函数。所以构建这样一个集合很容

易。我们希望这些函数提供这些功能时能够不污染全局命名空间,没有额外的开销,没有正确性问题。幸运的是

C++使用一种抽象mixin基类满足了我们的需要。
禁止堆对象:
禁止用户直接实例化对象很简单。因为总是调用new来建立这种对象,你能够禁止用户调用new。
(把operator new函数和operator delete声明为private。)
正像没有可移植的方法来判断地址是否在堆中一样,也没有可移植的方法判断地址是否不在堆中.
M28:灵巧(smart)指针
1.灵巧指针的构造、赋值和析构
构造通常很简单:找到指向的对象,让灵巧指针的内部成员dumb pointer指向它。如果没有找到对象,把内部指

针设为0或发出一个错误信号(可以是抛出一个异常)
使用灵巧指针与使用dump pointer没有很大的差别。这表明了封装是非常有效的。灵巧指针的用户可以象使用

dumb pointer一样使用灵巧指针
“当auto_ptr被拷贝和赋值时,对象所有权随之被传递”的方法,是一个更具灵活性的解决方案.
因为当调用auto_ptr的拷贝构造函数时,对象的所有权被传递出去,所以通过传值方式传递auto_ptr对象是一个

很糟糕的方法。通过const引用(Pass-by-reference-to-const)传递auto_ptr可以避免传值所产生的风险。
2.实现Dereference 操作符
灵巧指针的核心部分,operator*和operator-> 函数
template<class T>
T& SmartPtr<T>::operator*() const
{
  perform "smart pointer" processing;
  return *pointee;
}
返回引用兼顾正确性与效率。dereference一个空指针的结果是未定义的,所以随你怎么实现都不算错。
template<class T>
T* SmartPtr<T>::operator->() const
{
  perform "smart pointer" processing;
  return pointee;
}
3.测试灵巧指针是否为NULL
void*隐式的类型转换或operator!函数。
缺点:在一些情况下虽然大多数程序员希望它调用失败,但是函数确实能够成功地被调用(参见条款M5)。特别是它允许灵巧指针与完全不同的类型之间进行比较
4.把灵巧指针转变成dumb指针
这样做会引起很大的问题,除非有一个让人非常信服的原因去这样做,否则绝对不要提供转换到dumb指针的隐式类型转换操作符。
5.灵巧指针和继承类到基类的类型转换
在继承类向基类进行类型转换方面,我们如何能够让灵巧指针的行为与dumb指针一样呢?答案很简单:不可能。

正如Daniel Edelson所说,灵巧指针固然灵巧,但不是指针。最好的方法是使用成员模板生成类型转换函数,在会产生二义性结果的地方使用casts(类型转换,参见条款M2)。这不是一个完美的方法,不过已经很不错了,在一些情况下需去除二义性,所付出的代价与灵巧指针提供复杂的功能相比还是值得的。

声明(非虚)成员函数模板(通常就叫成员模板(member template)),你能使用它来生成灵巧指针类型转换函数
6.灵巧指针和const
灵巧指针只能在一个地方放置const,并只能对指针本身起作用,而不能针对于所指对象。
每个指向T的灵巧指针类public派生自一个对应的指向const-T的灵巧指针类。
总结:
问题:灵巧指针如此繁琐麻烦,是否值得使用,特别是如果你的编译器缺乏支持成员函数模板时。
通常是值得的。例如通过使用灵巧指针极大地简化了条款M29中的引用计数代码。而且正如该例子所显示的,灵巧指针的使用在一些领域受到极大的限制,例如测试空值、转换到dumb指针、继承类向基类转换和对指向const的指针的支持。同时灵巧指针的实现、理解和维护需要大量的技巧。调试使用灵巧指针的代码也比调试使用dumb指针的代码困难。无论如何你也不可能设计出一种通用目的的灵巧指针,能够替代dumb指针。
达到同样的代码效果,使用灵巧指针更方便。灵巧指针应该谨慎使用, 不过每个C++程序员最终都会发现它们是有用的。
M29:引用计数(太复杂了)
引用计数是这样一个技巧,它允许多个有相同值的对象共享这个值的实现.引用计数可以免除跟踪对象所有权的担子,因为当使用引用计数后,对象自己拥有自己。当没人再使用它时,它自己自动销毁自己。因此,引用计数是个简单的垃圾回收体系。
1.实现引用计数 2.写时拷贝 
3.带引用计数的基类
RCObject的定义如下:
class RCObject {
public:
  RCObject();
  RCObject(const RCObject& rhs);
  RCObject& operator=(const RCObject& rhs);
  virtual ~RCObject() = 0;
  void addReference();
  void removeReference();
void markUnshareable();
bool isShareable() const;
bool isShared() const;
private:
int refCount;
bool shareable;
};
RCOject的实现代码:
RCObject::RCObject()
: refCount(0), shareable(true) {}
RCObject::RCObject(const RCObject&)
: refCount(0), shareable(true) {}
>RCObject& RCObject::operator=(const RCObject&)
{ return *this; }
RCObject::~RCObject() {}               // virtual dtors must always
                                       // be implemented, even if
                                       // they are pure virtual
                                       // and do nothing (see also
                                       // Item M33 and Item E14)
void RCObject::addReference() { ++refCount; }
void RCObject::removeReference()
{   if (--refCount == 0) delete this; }
void RCObject::markUnshareable()
{ shareable = false; }
bool RCObject::isShareable() const
{ return shareable; }
bool RCObject::isShared() const
{ return refCount > 1; }
4.自动的引用计数处理
5.最后,我们将各个部分放在一起,构造一个基于可重用的RCObject和RCPtr类的带引用计数的String类
template<class T>                       // template class for smart
class RCPtr {                           // pointers-to-T objects; T
public:                                 // must inherit from RCObject
  RCPtr(T* realPtr = 0);
  RCPtr(const RCPtr& rhs);
  ~RCPtr();
  RCPtr& operator=(const RCPtr& rhs);
  T* operator->() const;
  T& operator*() const;
private:
  T *pointee;
  void init();
};
class RCObject {                       // base class for reference-
public:                                // counted objects
  void addReference();
  void removeReference();
  void markUnshareable();
  bool isShareable() const;
  bool isShared() const;
protected:
  RCObject();
  RCObject(const RCObject& rhs);
  RCObject& operator=(const RCObject& rhs);
  virtual ~RCObject() = 0;
private:
  int refCount;
  bool shareable;
};
class String {                           // class to be used by
public:                                  // application developers
  String(const char *value = "");
  const char& operator[](int index) const;
  char& operator[](int index);
private:
  // class representing string values
  struct StringValue: public RCObject {
    char *data;
    StringValue(const char *initValue);
    StringValue(const StringValue& rhs);
    void init(const char *initValue);
    ~StringValue();
  };
  RCPtr<StringValue> value;
};


RCObject::RCObject()
: refCount(0), shareable(true) {}
RCObject::RCObject(const RCObject&)
: refCount(0), shareable(true) {}
RCObject& RCObject::operator=(const RCObject&)
{ return *this; }
RCObject::~RCObject() {}
void RCObject::addReference() { ++refCount; }
void RCObject::removeReference()
{ if (--refCount == 0) delete this; }
void RCObject::markUnshareable()
{ shareable = false; }
bool RCObject::isShareable() const
{ return shareable; }
bool RCObject::isShared() const
{ return refCount > 1; }
这是RCPtr的实现:
template<class T>
void RCPtr<T>::init()
{
  if (pointee == 0) return;
  if (pointee->isShareable() == false) {
    pointee = new T(*pointee);
  }
  pointee->addReference();
}
template<class T>
RCPtr<T>::RCPtr(T* realPtr)
: pointee(realPtr)
{ init(); }
template<class T>
RCPtr<T>::RCPtr(const RCPtr& rhs)
: pointee(rhs.pointee)
{ init(); }
template<class T>
RCPtr<T>::~RCPtr()
{ if (pointee)pointee->removeReference(); }
template<class T>
RCPtr<T>& RCPtr<T>::operator=(const RCPtr& rhs)
{
  if (pointee != rhs.pointee) {
    if (pointee) pointee->removeReference();
    pointee = rhs.pointee;
    init();
  }
return *this;
}
template<class T>
T* RCPtr<T>::operator->() const { return pointee; }
template<class T>
T& RCPtr<T>::operator*() const { return *pointee; }
    这是String::StringValue的实现:
void String::StringValue::init(const char *initValue)
{
  data = new char[strlen(initValue) + 1];
  strcpy(data, initValue);
}
String::StringValue::StringValue(const char *initValue)
{ init(initValue); }
String::StringValue::StringValue(const StringValue& rhs)
{ init(rhs.data); }
String::StringValue::~StringValue()
{ delete [] data; }
最后,归结到String,它的实现是:
String::String(const char *initValue)
: value(new StringValue(initValue)) {}
const char& String::operator[](int index) const
{ return value->data[index]; }
char& String::operator[](int index)
{
  if (value->isShared()) {
    value = new StringValue(value->data);
  }
  value->markUnshareable();
  return value->data[index];
}
什么情况下使用引用计数:
少量的值被大量的对象共享。这样的共享通常通过调用赋值操作和拷贝构造而发生。对象/值的比例越高,越是适宜使用引用计数。
M30:代理类
1.实现二维数组
扮演其它对象的对象通常被称为代理类。使用proxy类区分operator[]作左值还是右值
M31:让函数根据一个以上的对象来决定怎么虚拟
如果你需要实现二重调度,最好的办法是修改设计以取消这个需要。


本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Boost中的智能指针--BoostProgrammSmartPoint
在你的游戏中应用LUA
C++深度探索系列:智能指针(Smart Pointer)
一个可识别联合(Discriminated Unions)的C++实现
C++ 模板基础谈 - C/C++ / C++ 语言
【转】智能指针学习
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服