Más notas de estudio eficaz Cinco C: Tips

1. La función constructora virtualizados y no miembros

considerar

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

En donde NLComponent algunas clases de derivados, readComponent diferentes objetos construyó de acuerdo con el flujo de entrada de lectura, conocida como "constructores virtuales".

También hay una "copia constructor virtual", por ejemplo,

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. una clase limita el número de objetos que puede ser generada

  • Prevent objeto instanciación  

    Los constructores en el interior privado.

  • Sólo una

     semifallo

La introducción del concepto de enlaces internos y enlaces externos.

Los enlaces internos:

1. estática (estática) definir variables globales, definición estática de las funciones gratuitas, definir funciones friend estáticas

 c ++ 11 propuesto el uso de un espacio de nombres en el anonimato en lugar de funciones de variable global estática, etc.
2. Definición de las clases 

(Definidos en el archivo de encabezado de clase (en línea definida) para dar un cpp diferentes que incluyen error no se produce para dar redefinición) (declaración defensiva puede ser retirado en el caso)
3. Una definición de función en línea  
definida 4.Union comunidad  
5.const constante define  
6. definición de tipo de enumeración  
7. Todas las declaraciones

(Declarada en el archivo de cabecera)

Enlaces externos:

1. Definición de la clase de función no-inline (incluidas las funciones miembro de la clase estática y funciones miembro) de  
la definición de las variables miembro estáticas de la clase 2.  
3. espacio de nombres o la erradicación mundial de la función no estático, variable no estática, la función no-amigo definición

Se define en el CPP?

  • Limitar el número de objetos
//.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;
}

 

  • La creación de objetos del entorno

En la herencia de clases o diseños complejos, por encima del cual no puede cumplir con las expectativas.

Si una clase constructor privado no puede ser heredada, que no se puede incrustar en otras clases.

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);}
  • Objetos permiten libertad de movimiento
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);}
  • Tiene una función recuento de objetos de la clase base
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 genera un objeto en el montón o prohibir

 

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

En este caso, UPNumber * = numberArray nueva UPNumber [100]; habrá un problema, porque sólo una memoria de aplicaciones

UPNumber * pn = new UPNumber (* nuevo UPNumber); también tienen problemas de secuenciación problema

 

Nota: el espacio de direcciones del programa desde la parte superior de la pila de extensión detalle, desde la parte inferior de la pila extendida hacia arriba.

Una idea: utilizar la comparación de direcciones fiables. (No portátiles)

 

Aquí es una implementación:

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();
}
  • Prohibir objeto montón

Aún teniendo en cuenta, directamente instancia, la clase derivada se crea una instancia y se incrusta tres casos.

En el primer caso, directamente al operador nuevo y operador de eliminación declarados como privado.

operador nuevo y operador de eliminación se heredan de forma automática, si no reescrito para el público en una clase derivada, todavía es una versión privada.

El último caso

class Assert
{
public:
    Assert(int inValue);

private:
    UPNumber value;
};


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

 

4.smart puntero

//todo

El contador de referencias

(Modelo de recuento de referencia y entender el modelo de propiedad)

recuento de referencias sistema de dos motivaciones simple recogida de basura 1. 2. operación de multiplexación clase cadena similar

  • Implementar el recuento de referencias
//
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;
}
  • Al escribir la copia

1. Para lograr una sobrecarga operación índice de matriz de sólo lectura y de escritura.

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

 

  • Puntero, de referencia y de copia por escritura

Hay un problema: si S1 y S2 compartir una cadena, un puntero a la cadena, el constructor copia de cadena no modifica la cadena puede ser detectado por este puntero.

Valor mediante la adición de un operador no const versiones bool operador [] se bool valor se establece en false; se juzga si el valor bool constructor de copia es, siempre nuevos datos de configuración falsos.

  • Con referencia a la clase base recuento

 

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

 

  • proceso de recuento de referencia automática

El uso de punteros inteligentes mango

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; }
  • junto
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];
}
  • Aumentar con el número de referencia de clase existente

Eso puede no hereda directamente de RCObject, RCPtr no se puede utilizar directamente.

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

considerar

datos int [10] [20]; // legítimos

Si utiliza una dimensión variable en lugar de una constante si no es legítimo.

  • Lograr la matriz bidimensional
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;

};
  • El juicio es de escritura o la operación de lectura

O por no const const sobrecargado versión del operador [] no puede ser determinado lector, y porque sólo hay una propiedad de la relación de objeto 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;
}

 

  • limitación

1.String s1 = "Hola";

   char * p = & s1 [1];

Sobrecarga del operador tiene que tomar la dirección de la clase de proxy este caso de uso.

 

2. La clase de plantilla, [] devuelve un tipo de clase de proxy en lugar del tipo real

3. El caso en el que el número de conversión implícita, la consideración explícita

 

 

 

6. decidir cómo hacer la función virtual de acuerdo a más de un objetivo

Pregunta: ¿cómo llamar a una función asociada a dos o más tipo de parámetro de entrada, el problema de "doble expedición".

  • Con la adición de las funciones virtuales RTTI

determinar si otra cosa abandonar el uso del paquete typeid.

  • Usando solamente funciones virtuales

Cada tipo de derivada de una función.

 

funciones virtuales RTTI que el método es seguro, pero limita la capacidad de control del programa, por el método de RTTI mano no requiere recompilación, pero a menudo resulta en código que no se puede mantener.

 

  • tabla de función virtual
//...
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)
{
	//
}

//...
  • Inicialización de una tabla de función virtual

El uso de objetos estáticos locales se inicializa una sola vez

  • El uso de los no miembros de gestión de colisiones
  • La herencia y la tabla de funciones virtuales

 

 

 

 

Publicado 44 artículos originales · ganado elogios 5 · Vistas 1393

Supongo que te gusta

Origin blog.csdn.net/qq_33776188/article/details/104653254
Recomendado
Clasificación