Efectivo C ++ cláusula 14_ más que esto

Tenga cuidado con el comportamiento de copia en las clases de gestión de recursos

La cláusula 13 introduce el concepto: "La adquisición de recursos es inicialización; RAII" (La adquisición de recursos es inicialización; RAII), y lo utiliza como la columna vertebral de la "categoría de gestión de recursos". También describe cómo auto_ptr y tr1 :: shared_ptr integran este concepto Expresado en recursos basados ​​en montón (basados ​​en montón).
Sin embargo, no todos los recursos están basados ​​en montones. Para esos recursos, los punteros de comando anteriores a menudo no son adecuados para la administración de recursos.
Siendo ese el caso, a veces necesitamos crear nuestras propias clases de gestión de recursos.
Por ejemplo:
Supongamos que la función API de C se utiliza para procesar objetos mutex de tipo Mutex. Hay dos funciones disponibles: bloquear y desbloquear:

void lock(Mutex* ptm);    // 锁定 ptm 所指的互斥器
void unlock(Mutex* ptm);	// 解除互斥器锁定

Para asegurarse de que cada Mutex bloqueado esté desbloqueado, es posible que desee crear una clase para administrar el bloqueo de la máquina.
La estructura básica de dicha clase se rige por el código RAII: los recursos se adquieren durante la construcción y se liberan durante la destrucción.

class Lock{
    
    
public:
	explicit Lock(Mutex* ptm): mutexPtr(pm){
    
    
		lock(mutexPtr);			// 获得资源
		}
	~Lock(){
    
     unlock(mutexPtr); }    // 释放资源
private:
	Mutex* mutexPtr;
};

El uso de Lock por parte del cliente se ajusta al método RAII:

Mutex n;			// 定义需要的互斥器
...
{
    
    				// 建立一个区块用来定义 critical section(关键部分)
Lock ml(&n);	// 锁定互斥器
...				// 执行 critical section 内的操作
}

Esto es muy bueno, pero ¿qué sucede si se copia el objeto Bloquear?

Lock ml1(&n);		// 锁定n
Lock ml2(ml1);		// ml1 复制到 ml2 身上,结果怎样。

Ésta es una pregunta común: ¿qué sucede cuando se copia un objeto RAII? En la mayoría de los casos, existen dos opciones:

  • Está prohibido copiar . Muchas veces no es razonable permitir que se copien objetos RAII. Porque es raro tener un adjunto razonable (copia) del "artefacto básico sincronizado". Entonces deberíamos prohibirlo.

El ítem 06 nos dice qué hacer: declarar la operación de copia como privada. Para Lock es así:

class Lock: private Uncopyable{
    
    		// 禁止复制。见条款 06
	...
};
  • Ofreciendo un "método de recuento de referencias" a los recursos subyacentes . A veces queremos conservar un recurso hasta que se destruya el último objeto que lo usa. En este caso, se puede copiar el objeto RAII y se debe incrementar el "número de referencia" del recurso. Esto es cierto para tr1 :: shared_ptr.

Generalmente, siempre que contenga una variable miembro tr1 :: shared_ptr, las clases RAII pueden implementar la copia de recuento de referencias.
Si el bloqueo mencionado anteriormente tiene la intención de utilizar el recuento de referencias, puede cambiar el tipo de mutexPtr cambiándolo de Mutex * a tr1 :: shared_ptr <\ Mutex>. (No hay \ en <>, porque Mutex no se puede mostrar como una etiqueta de Markdown incorporada , Entonces agregue "\").
Desafortunadamente, el comportamiento predeterminado de tr1 :: shared_ptr es "eliminar a lo que se refiere cuando el número de referencias es 0", que no es el comportamiento que queremos. Cuando usamos un Mutex, la acción de liberación que queremos hacer es desbloquearlo en lugar de eliminarlo.
Afortunadamente, tr1 :: shared_ptr le permite especificar el llamado "borrador" (borrador), que es una función u objeto de función que se llama cuando el número de referencias es 0 (auto_ptr no tiene dicha función, siempre Su puntero se elimina). El borrador es un segundo parámetro opcional del constructor tr1 :: shared_ptr, por lo que el código se ve así:

class Lock{
    
    
public:
	explicit Lock(Mutex* ptm): mutexPtr(ptm, unlock){
    
        // 以某个 Mutex 初始化 shared_ptr 并以 unlock 函数为删除器
		lock(mutexPtr.get());      // 条款15谈 get()
	}
private:
	std::tr1::shared_ptr<Mutex> mutexPtr;    // 使用 shared_ptr 替换 raw(未加工) pointer
};

Tenga en cuenta que la clase Lock en este ejemplo ya no declara un destructor. El destructor de clases llamará automáticamente al destructor de su variable miembro no estática (mutexPtr), y el destructor de mutexPtr llamará automáticamente al eliminador de tr1 :: shared_ptr cuando el número de referencias al mutex sea 0 (este ejemplo Para desbloquear). Si lo piensa, encontrará que no ha olvidado la destrucción, simplemente confíe en el comportamiento predeterminado generado por el compilador.

por favor recuerde:

  • La copia de objetos RAII también debe copiar los recursos que administra, por lo que el comportamiento de copia de los recursos determina el comportamiento de copia de los objetos RAII.
  • Los comportamientos de copia comunes y comunes de la clase RAII son: inhibir la copia e implementar el recuento de referencias. Pero también se pueden implementar otros comportamientos.

Supongo que te gusta

Origin blog.csdn.net/weixin_48033173/article/details/109100938
Recomendado
Clasificación