1.将构造函数和非成员函数虚拟化
·考虑
class NewsLetter
{
public:
NewsLetter(istream& str);
...
private:
static NLComponent* readComponet(istream& str);
private:
list<NLComponent*> components;
}
NewsLetter::NewsLetter(istream& str)
{
while(str)
{
components.push_back(readComponent(str));
}
}
其中NLComponent派生出一些类,readComponent根据读入流构造出不同的对象,称之为“虚拟构造函数”。
还有一种“虚拟拷贝构造函数”,例如
class NLComponent
{
public:
virtual NLComponent* clone() const = 0;
};
class TextBlock: public NLComponent
{
public:
virtual TextBlock * clone() const
{
return new TextBlock(*this);
}
};
class Graphic: public NLComponent{
public:
virtual Graphic* clone() const
{
return new Graphic(*this);
}
};
///////////////////////////////
class NewsLetter{
public:
NewsLetter(const NewsLetter* rhs);
...
private:
list<NLComponent*> components;
}
NewsLetter::NewsLetter(const NewsLetter& rhs)
{
for(list<NLComponent*>::const_iterator it = rhs.components.begin(); it != rhs.components.end(); ++ it)
{
components.push_back((*it) ->clone())
}
}
2.限制某个类所能产生的对象数量
- 阻止对象实例化
将构造函数放在private里。
- 只允许一个
单例模式
引入 内部链接和外部链接的概念。
内部链接:
1.静态(static)全局变量的定义、静态自由函数的定义、静态友元函数的定义
c++11建议用匿名命名空间 代替静态全局变量 函数等等
2.类的定义
(在头文件中定义类(定义内联函数) 不同得cpp文件include它 不会发生重定义得错误) (去掉防卫式声明得情况下)
3.内联函数定义
4.Union共同体定义
5.const常量定义
6.枚举类型定义
7.所有的声明
(在头文件里声明)
外部链接:
1.类的非内联函数(包括成员函数和静态类成员函数)的定义
2.类的静态成员变量的定义
3.名字空间或全局的非静态的自由函数,非静态变量,非友元函数的定义
定义在cpp里 ?
- 限制对象数量
//.h
class Printer
{
public:
class TooManyObjects{};
Printer();
~Printer();
...
private:
static size_t numObjects;
Printer(const Printer& rhs);
}
//.cpp
size_t Printer::numObjects = 0;
Printer::Printer()
{
if(numObjects >= 1)
{
throw TooManyObjects();
}
++numObjects;
}
Printer::~Printer()
{
--numObjects;
}
- 建立对象的环境
在继承或者复合的类设计中,上面这种可能会不符合期望。
如果使用private构造函数的类,则不能被继承,也不能被嵌入其他类中。
class FSA
{
public:
static FSA* makeFSA();
static FSA* makeFSA(const FSA& rhs);
...
private:
FSA();
FSA(const FSA& rhs);
}
FSA* FSA::makeFSA()
{return new FSA();}
FSA* FSA::makeFSA(const FSA& rhs)
{return new FSA(rhs);}
- 允许对象来去自由
class Printer
{
public:
class TooManyObjects{};
static Printer* makePrinter();
static Printer* makePrinter();
...
private:
static size_t numObjects;
static const size_t maxObjects = 10;
Printer();
Printer(const Printer& rhs);
};
//
size_t Printer::numObjects = 0;
const size_t Printer::maxObjects;
Printer::Printer()
{
if(numObjects >= maxObjects)
throw TooManyObjects();
...
}
Printer::Printer(const Printer& rhs)
{
if(numObjects >= maxObjects)
throw TooManyObjects();
...
}
Printer* Printer::makePrinter()
{ return new Printer;}
Printer* Printer::makePrinter(const Printer& rhs)
{return new Printer(rhs);}
- 一个具有对象计数功能的基类
template<typename 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<typename BeingCounted>
Counted<BeingCounted>::Counted()
{
init();
}
template<typename BeingCounted>
Counted<BeingCounted>::Counted(const Counted<BeingCounted>&)
{
init();
}
template<typename BeingCounted>
void Counted<BeingCounted>::init()
{
if (numObjects >= maxObjects) throw TooManyObjects();
++numObjects;
}
template<typename BeingCounted>
int Counted<BeingCounted>::numObjects = 0;
//class PrintJob;
class Printer : private Counted<Printer>
{
public:
static Printer* makePrinter();
static Printer* makePrinter(const Printer& rhs);
~Printer();
//void submieJob(const PrintJob& job);
void reset();
void performSelfTest();
//...
using Counted<Printer>::objectCount; //恢复访问权
using Counted<Printer>::TooManyObjects;
private:
Printer();
Printer(const Printer& rhs);
};
3.要求或禁止在堆中产生对象
考虑一种想法:
class UPNumber
{
public:
class HeapConstraintViolation {};
static void* operator new(size_t size);
UPNumber();
private:
static bool onTheHeap;
};
bool UPNumber::onTheHeap = false;
void* UPNumber::operator new(size_t size)
{
onTheHeap = true;
return ::operator new(size);
}
UPNumber::UPNumber()
{
if (!onTheHeap)
{
throw HeapConstraintViolation();
}
onTheHeap = false;
}
这种情况下 , UPNumber* numberArray = new UPNumber[100]; 会出现问题 ,因为只会申请一次内存
UPNumber* pn = new UPNumber(*new UPNumber); 也会有问题 顺序问题
注:程序的栈从地址空间顶部详下扩展,堆则从底部向上扩展。
一种想法:利用地址对比,不可靠。(不具有可移植性)
下面是一种实现:
class HeapTracked
{
public:
class MissingAddress {};
virtual ~HeapTracked() = 0;
static void *operator new(size_t size);
static void operator delete(void *ptr);
bool isOnHeap() const;
private:
typedef const void* RawAddress;
static list<RawAddress> address;
};
//impletation
typedef const void* RawAddress;
list<RawAddress> HeapTracked::address;
HeapTracked::~HeapTracked() {}
void* HeapTracked::operator new(size_t size)
{
void* memPtr = ::operator new(size);
address.push_front(memPtr);
return memPtr;
}
void HeapTracked::operator delete(void* ptr)
{
list<RawAddress>::iterator it = find(address.begin(), address.end(), ptr);
if (it != address.end())
{
address.erase(it);
::operator delete(ptr);
}
else
{
throw MissingAddress();
}
}
bool HeapTracked::isOnHeap() const
{
const void* rawAddress = dynamic_cast<const void*>(this);
list<RawAddress>::iterator it = find(address.begin(), address.end(), rawAddress);
return it != address.end();
}
- 禁止堆对象
依旧考虑,直接实例化,派生类实例化和内嵌三种情况。
对于第一种情况,直接将operator new 和 operator delete声明为private。
operator new和operator delete是自动继承的,如果在派生类中没有重写为public,则依旧是private版本。
而最后一种情况
class Assert
{
public:
Assert(int inValue);
private:
UPNumber value;
};
Assert *pa = new Assert(100); // 调用Assert的operation new或者 ::operator new 不是 UPNumber的
4.smart Pointer
//todo
5.引用计数
(了解引用计数模型 和 所有权模型)
使用引用计数两个动机 1.简单的垃圾回收体系 2.复用 类似 string类的操作
- 实现引用计数
//
class String
{
public:
String(const String* rhs);
String(const char* initValue = "");
~String();
String& operator=(const String& rhs);
private:
struct StringValue
{
int refCount;
char* data;
StringValue(const char* initValue);
~StringValue();
};
StringValue* value;
};
String::StringValue::StringValue(const char* initValue)
:refCount(1)
{
data = new char[strlen(initValue) + 1];
strcpy(data, initValue);
}
String::StringValue::~StringValue()
{
delete[] data;
}
String::String(const char* initValue)
:value(new StringValue(initValue))
{
}
String::String(const String& rhs)
:value(rhs.value)
{
++value->refCount;
}
String::~String()
{
if (--value->refCount == 0) delete value;
}
String& String::operator=(const String& rhs)
{
if (value == rhs.value)
return *this;
if (--value->refCount == 0)
delete value;
value = rhs.value;
++value->refCount;
return *this;
}
- 写时拷贝
1.实现数组的下标操作 重载 只读和可写。
const char& operator[](int _Index) const;//const
char& operator[](int _Index);//non-const
//
const char& String::operator[](int index) const {
return value->data[index];
}
//写入操作 不能破坏共享的data
char& String::operator[](int index)
{
if (value->refCount > 1)
{
--value->refCount;
value = new StringValue(value->data);
}
return value->data[index];
}
- 指针,引用与写时拷贝
存在一个问题: 若s1 和 s2 共享一个字符串,有一个指针指向这个字符串,通过这个指针修改字符串并不能被string的拷贝构造函数检测出来。
可以通过添加bool值 在非const版本operator[]操作中将bool值置为false; 拷贝构造里判断如果该bool值为false,则总是新构造数据。
- 带引用计数的基类
class RCObject
{
public:
RCObject();
RCObject(const RCObject& rhs);
RCObject& operator=(const RCObject& rhs);
virtual ~RCObject() = 0;
void addReference();
void removeReference();
void makeUnShareable();
bool isShareable() const;
bool isShared() const;
private:
int refCount;
bool shareable;
};
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::makeUnShareable() { shareable = false; }
bool RCObject::isShareable() const { return shareable; }
bool RCObject::isShared() const { return refCount > 1; }
- 自动的引用计数处理
利用智能指针处理
template<class T>
class RCPtr
{
public:
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();
};
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>
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>::operator=(const RCPtr& rhs)
{
if (pointee != rhs.pointee)
{
if (pointee)
{
pointee->removeReference();
}
pointee = rhs.pointee;
init();
}
return *this;
}
template<class T>
RCPtr<T>::~RCPtr()
{
if (pointee) pointee->removeReference();
}
template<class T>
T* RCPtr<T>::operator->() const { return pointee; }
template<class T>
T& RCPtr<T>::operator*() const { return *pointee; }
- 合在一起
template<class T>
class RCPtr {
public:
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
{
public:
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 {
public:
String(const char* value = "");
const char& operator[](int index) const;
char& operator[](int index);
private:
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;
}
//
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(realPtr)
{
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; }
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(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];
}
- 在现存类上增加引用计数
即不能直接从RCObject继承,也不能直接使用RCPtr。
template<class T>
class RCIPtr
{
public:
RCIPtr(T* realPtr = 0);
RCIPtr(const RCIPtr& rhs);
~RCIPtr();
RCIPtr& operator=(const RCIPtr& rhs);
const T* operator->() const;
T* operator->();
const T& operator*() const;
T& operator*();
private:
struct CountHolder : public RCObject
{
~CountHolder() { delete pointee; }
T* pointee;
};
CountHolder* counter;
void init();
void makeCopy();
};
template<class T>
void RCIPtr<T>::init()
{
if (counter->isShareable() == false)
{
T* oldValue = counter->pointee;
counter = new CountHolder;
counter->pointee = new T(*oldValue);
}
counter->addReference();
}
template<class T>
RCIPtr<T>::RCIPtr(T* realPtr)
:counter(new CountHolder)
{
counter->pointee = realPtr;
init();
}
template<class T>
RCIPtr<T>::RCIPtr(const RCIPtr& rhs)
:counter(rhs.counter)
{
init();
}
template<class T>
RCIPtr<T>::~RCIPtr()
{
counter->removeReference();
}
template<class T>
RCIPtr<T>& RCIPtr<T>::operator=(const RCIPtr& rhs)
{
if (counter != rhs.counter)
{
counter->removeReference();
counter = rhs.counter;
init();
}
return *this;
}
template<class T>
void RCIPtr<T>::makeCopy()
{
if (counter->isShared())
{
T* oldvalue = counter->pointee;
counter->removeReference();
counter = new CountHolder;
counter->pointee = new T(*oldvalue);
counter->addReference();
}
}
template<class T>
const T* RCIPtr<T>::operator->() const {
return counter->pointee;
}
template<class T>
T* RCIPtr<T>::operator->()
{
makeCopy();
return counter->pointee;
}
template<class T>
const T& RCIPtr<T>::operator*() const
{
return *(counter->pointee);
}
template<class T>
T& RCIPtr<T>::operator*()
{
makeCopy();
return *(counter->pointee);
}
class Widget
{
public:
Widget(int size);
Widget(const Widget& rhs);
~Widget();
Widget& operator=(const Widget& rhs);
void doThis();
int showThat() const;
};
class RCWIdget
{
public:
RCWIdget(int size) :value(new Widget(size)) {}
void doThis() { value->doThis(); }
int showThat() const { return value->showThat(); }
private:
RCIPtr<Widget> value;
};
6.代理类
考虑
int data[10][20];//合法
如果维数使用变量而不是常量的话则不合法。
- 实现二维数组
template<class T>
class Array2D
{
public:
class Array1D
{
T& operator[](int index);
const T& operator[](int index) const;
};
Array1D operator[](int index);
const Array1D operator[](int index) const;
};
- 判断是写操作还是读操作
通过重载const或者非const版本的[]操作符不能判断读写,因为只和对象的const性质有关系。
class String
{
public:
class CharProxy
{
public:
CharProxy(String& str, int index);
CharProxy& operator=(const CharProxy* rhs);
CharProxy& operator=(char c);
operator char() const;
private:
String& theString;
int charIndex;
};
const CharProxy operator[](int index) const;
CharProxy operator[](int index);
friend class CharProxy;
private:
RCPtr<StringValue> value;
};
const String::CharProxy String::operator[](int index) const
{
return CharProxy(const_cast<String&>(*this), index);
}
String::CharProxy String::operator[](int index)
{
return CharProxy(*this, index);
}
String::CharProxy::operator char() const {
return theString.value->data[charIndex];
}
String::CharProxy& String::CharProxy::operator=(const CharProxy& rhs)
{
if (theString.value->isShared())
theString.value = new StringValue(theString.value->data);
theString.value->data[charIndex] = rhs.theString.value->data[rhs.charIndex];
return *this;
}
- 局限性
1.String s1 = "Hello";
char *p = &s1[1];
这种使用情况下需要重载代理类的取地址操作符。
2.模板类中,[]返回的是代理类类型而不是实际的类型
3.一些隐式转换的情况下,考虑explicit
6.让函数根据一个以上的对象决定怎么虚拟
问题:函数怎么调用 与两个及以上的传入参数类型有关系,“二重调度”问题。
- 用虚函数加RTTI
if else 利用typeid判断 放弃封装。
- 只使用虚函数
每种类型的都派生一个函数。
虚函数比RTTI的方法安全,但它限制了程序的可控性,另一方面RTTI的方法不需要重编译,但通常会导致代码无法维护。
- 模拟虚函数表
//...
class GameObject
{
public:
virtual void collide(GameObject& otherObject) = 0;
};
class SpaceShip : public GameObject
{
public:
virtual void collide(GameObject& otherObject);
virtual void hitSpaceShip(SpaceShip& otherObject);
virtual void hitSpaceStation(SpaceStation& otherObject);
virtual void hitAsteroid(Asteroid& otherObject);
private:
typedef void(SpaceShip::*HitFunctionPtr)(GameObject&);
typedef map<string, HitFunctionPtr> HitMap;
static HitFunctionPtr lookup(const GameObject& whatWeHit);
};
SpaceShip::HitFunctionPtr SpaceShip::lookup(const GameObject& whatWeHit)
{
static HitMap collisionMap;
HitMap::iterator mapEntry = collisionMap.find(typeid(whatWeHit).name());
if (mapEntry == collisionMap.end()) return 0;
return (*mapEntry).second; //stl using type
}
void SpaceShip::collide(GameObject& otherObject)
{
HitFunctionPtr hfp = lookup(otherObject);
if (hfp)
{
(this->*hfp)(otherObject);
}
else
{
//...
}
}
void SpaceShip::hitSpaceShip(SpaceShip& otherObject)
{
//
}
//...
- 初始化模拟虚函数表
利用局部静态对象,只初始化一次
- 使用非成员的碰撞处理函数
- 继承与模拟虚函数表