More Effective C 学习笔记 五:技巧

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)
{
	//
}

//...
  • 初始化模拟虚函数表

利用局部静态对象,只初始化一次

  • 使用非成员的碰撞处理函数
  • 继承与模拟虚函数表
发布了44 篇原创文章 · 获赞 5 · 访问量 1393

猜你喜欢

转载自blog.csdn.net/qq_33776188/article/details/104653254
今日推荐