More Effective C study notes Five: Tips

1. The constructor function virtualized and non-members

·consider

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));
    }
}

Wherein NLComponent some derived classes, readComponent different objects constructed according to the read inflow, known as the "virtual constructors."

There is also a "virtual copy constructor", e.g.

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. a class limits the number of objects that can be generated

  • Prevent object instantiation  

    The constructors in private inside.

  • Only one

     Singleton

Introducing the concept of internal links and external links.

Internal links:

1. Static (static) define global variables, static definition of free functions, define static friend functions

 c ++ 11 proposed to use an anonymous namespace instead of static global variable functions, etc.
2. Definition of classes 

(Defined in the class header file (inline defined) to give a different cpp files that include error does not occur to give redefinition) (Defensive declaration may be removed in the case)
3. An inline function definition  
4.Union community defined  
5.const constant defined  
6. enum type definition  
7. All statements

(Declared in the header file)

external link:

1. Definition of the class of non-inline function (including static class member functions and member functions) of  
the definition of static member variables of the class 2.  
3. namespace or global freedom of non-static function, non-static variable, non-friend function Definition

Defined in cpp?

  • Limit the number of objects
//.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;
}

 

  • The establishment of environmental objects

In the class inheritance or complex designs, above which it may not meet expectations.

If a private constructor class can not be inherited, it can not be embedded in other classes.

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);}
  • Objects allow freedom of movement
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);}
  • Having a base class object count function
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. A generates an object in the heap or prohibit

 

Consider an idea:

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;
}

In this case, UPNumber * numberArray = new UPNumber [100]; there will be a problem, because only one application memory

UPNumber * pn = new UPNumber (* new UPNumber); also have problems sequencing problem

 

Note: the program address space from the top of the stack extension detail, from the bottom of the stack upwardly extended.

One idea: use the address comparison unreliable. (Not portable)

 

Here is an implementation:

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();
}
  • Prohibit heap object

Still considering, directly instantiated, the derived class is instantiated and embedded three cases.

In the first case, directly to the operator new and operator delete declared as private.

operator new and operator delete are automatically inherited, if not rewritten for the public in a derived class, it is still a private version.

The last case

class Assert
{
public:
    Assert(int inValue);

private:
    UPNumber value;
};


Assert *pa = new Assert(100); // 调用Assert的operation new或者 ::operator new 不是 UPNumber的

 

4.smart Pointer

//everything

The reference count

(Reference counting model and understand the ownership model)

Reference counting two motivations simple garbage collection system 1. 2. multiplexing operation similar string class

  • Implement reference counting
//
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;
}
  • When writing copy

1. To achieve an array index operation overloading read-only and writable.

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];
}

 

  • Pointer, reference and copy-on-write

There is a problem: if s1 and s2 sharing a string, a pointer to the string, the string copy constructor does not modify the string can be detected by this pointer.

Value by adding a non-const bool versions operator [] operator will bool value is set to false; it is judged if the copy constructor bool value is false, always new configuration data.

  • With reference to the base class count

 

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; }

 

  • Automatic reference counting process

The use of smart pointers handle

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; }
  • put them together
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];
}
  • Increase over existing class reference count

That can not inherit directly from RCObject, RCPtr can not be used directly.

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. Acting Class

consider

int data [10] [20]; // legitimate

If you use a variable dimension instead of a constant if not legitimate.

  • Achieve two-dimensional array
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;

};
  • Judgment is write or read operation

Or by non-const const overloaded version of operator [] can not be determined reader, and because there is only a property of the object const relationship.

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;
}

 

  • limitation

1.String s1 = "Hello";

   char *p   = &s1[1];

Overload operator needs to take the address of the proxy class this use case.

 

2. The template class, [] returns a proxy class type rather than the actual type

3. The case where the number of implicit conversion, explicit consideration

 

 

 

6. decide how to make the virtual function according to more than one target

Question: how to call a function associated with two or more incoming parameter type, "double dispatch" problem.

  • With the addition of virtual functions RTTI

determining if else abandon using typeid package.

  • Using only virtual functions

Each type of derivative of a function.

 

Virtual functions RTTI than the method is safe, but it limits the controllability of the program, on the other hand RTTI method does not require recompilation, but often results in code that can not be maintained.

 

  • Virtual function table
//...
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)
{
	//
}

//...
  • Initialization of a virtual function table

Using local static object is initialized only once

  • The use of non-members of collision handler
  • Inheritance and virtual function table

 

 

 

 

Published 44 original articles · won praise 5 · Views 1393

Guess you like

Origin blog.csdn.net/qq_33776188/article/details/104653254