2012-07-15 12 views
6

A partir de la discusión de esta pregunta How is access for private variables implemented in C++ under the hood?, planteé una variación: en lugar de acceder a un miembro de datos privados, ¿puede uno llamar a funciones de miembros privados a través de la transmisión y confiar en la compatibilidad de diseño?¿Se puede acceder a las funciones de miembros privados a través de conversión a tipos compatibles con el diseño?

Parte del código (inspirado en la columna de Herb Sutter Uses and Abuses of Access Rights)

#include <iostream> 

class X 
{ 
public: 
    X() : private_(1) { /*...*/ } 

private: 
    int Value() { return private_; } 
    int private_; 
}; 

// Nasty attempt to simulate the object layout 
// (cross your fingers and toes). 
// 
class BaitAndSwitch 
    // hopefully has the same data layout as X 
{ // so we can pass him off as one 
public: 
    int Value() { return private_; } 
private: 
    int private_; 
}; 

int f(X& x) 
{ 
    // evil laughter here 
    return (reinterpret_cast<BaitAndSwitch&>(x)).Value(); 
} 

int main() 
{ 
    X x; 
    std::cout << f(x) << "\n"; // prints 0, not 1 
    return 0; 
}; 

Nota: esto funciona (al menos en Ideone)! ¿Hay alguna manera de que el nuevo C++11 Standard proporcione un garantizado o al menos un definido por la implementación forma de eludir el control de acceso confiando en la compatibilidad de diseño y reinterpret_cast/static_cast?

EDIT1: salida de Ideone

Edit2: En la columna de Sutter enumera dos razones por las que el código anterior no está garantizada para trabajar (aunque funciona en la práctica)

un

) Los diseños de objetos de X y BaitAndSwitch no tienen la garantía de ser iguales, aunque en la práctica probablemente siempre lo sean.

b) Los resultados de la reinterpret_cast no están definidos, aunque la mayoría de los compiladores permitirán que intenta utilizar la referencia de resultado en la forma el pirateo informático destinada.

¿El nuevo C++ 11 Standard ahora proporciona estas garantías de diseño/reinterpretar_cast?

+0

Puede evitar casi cualquier cosa que desee con 'reinterpret_cast'; ¿Estás preguntando si es posible hacerlo de una manera bien definida? –

+0

@OliCharlesworth Sí, las garantías serían las mejores, la implementación definida como la segunda mejor opción y la UB no tan buena. – TemplateRex

+0

De ninguna manera. El método de clase de nivel de ensamblado es solo una función global con el parámetro "this" oculto, no pertenece a la instancia de clase y no se puede acceder a través de trucos de diseño. –

Respuesta

3

Sí, podría crear un tipo que use el mismo diseño que el tipo del que está tratando de robar, luego reinterpret_cast de ese tipo para su tipo de diseño compatible. Pero esto solo está protegido por el estándar si ambos los tipos de fuente y destino son tipos de disposición estándar (y, por supuesto, solo funciona si sus diseños son los mismos). Entonces, si la fuente tiene funciones virtuales, estás jodido.

Esto parece satisfacer los dos problemas de Sutter aquí. Las reglas del diseño estándar aseguran que dos tipos que son ambos diseños estándar que definen los mismos miembros en el mismo orden son compatibles con el diseño (sección 9.2, párrafo 17):

Dos estructura de disposición estándar (Cláusula 9) los tipos son compatibles con el diseño si tienen el mismo número de miembros de datos no estáticos y los miembros de datos no estáticos correspondientes (en orden de declaración) tienen tipos compatibles con el diseño (3.9).

y las reglas para reinterpret_cast especifican el significado de la conversión entre dos tipos estándar de trazado (sección 5.2.10, párrafo 7):

un puntero de objeto se puede convertir explícitamente a un puntero de objeto de un tipo diferente Cuando un prvalue v del tipo "puntero a T1" se convierte al tipo "puntero a cv T2", el resultado es static_cast<cv T2*>(static_cast<cv void*>(v)) si tanto T1 como T2 son tipos de disposición estándar (3.9) y los requisitos de alineación de T2 no son más estrictos que los de T1, o si cualquiera de los tipos es nulo.

+0

Gracias, ya corrigió la declaración de devolución faltante. – TemplateRex

+2

¿Eso no viola el alias estricto? – Puppy

+0

@DeadMG: No estoy seguro. Revisé [algunas de las secciones del estándar que causan un aliasing estricto] (http://stackoverflow.com/a/9588075/734069), y esto parece no violarlas. Pero no soy un experto en eso. –

Cuestiones relacionadas