¿Existe una razón específica por la que esto rompería el lenguaje conceptualmente o una razón específica por la que esto es técnicamente inviable en algunos casos?
El uso sería con un nuevo operador.
Editar: Voy a renunciar a la esperanza de tener a mi "nuevo operador" y "operador nuevo" directamente y ser directo.
El punto de la pregunta es: ¿por qué los constructores son especiales ? Tenga en cuenta, por supuesto, que las especificaciones de idioma nos dicen qué es legal, pero no necesariamente moral. Lo que es legal generalmente se informa por lo que es lógicamente consistente con el resto del lenguaje, lo que es simple y conciso, y lo que es factible que los compiladores implementen. La posible justificación del comité de normas para sopesar estos factores es deliberada e interesante, de ahí la pregunta.
std::make_unique
ystd::make_shared
puede resolver adecuadamente la motivación práctica subyacente para esta pregunta. Estos son métodos de plantilla, lo que significa que uno necesita capturar los argumentos de entrada para el constructor y luego reenviarlos al constructor real.Respuestas:
Las funciones de punteros a miembro solo tienen sentido si tiene más de una función de miembro con la misma firma; de lo contrario, solo habría un valor posible para su puntero. Pero eso no es posible para los constructores, ya que en C ++ diferentes constructores de la misma clase deben tener firmas diferentes.
La alternativa para Stroustrup hubiera sido elegir una sintaxis para C ++ donde los constructores pudieran tener un nombre diferente al nombre de la clase, pero eso habría evitado algunos aspectos muy elegantes de la sintaxis ctor existente y habría complicado el lenguaje. Para mí, eso parece un precio alto solo para permitir una característica rara vez necesaria que se puede simular fácilmente mediante la "subcontratación" de la inicialización de un objeto desde el ctor a una
init
función diferente (una función miembro normal para la que se puede puntero a miembros creado).fuente
memcpy(buffer, (&std::string)(int, char), size)
? (Probablemente extremadamente poco kosher, pero esto es C ++ después de todo).memcpy(buffer, strlen, size)
. Presumiblemente copiaría la asamblea, pero quién sabe. Si se puede invocar o no el código sin fallar, se requiere conocimiento sobre el compilador que utiliza. Lo mismo vale para determinar el tamaño. Sería muy dependiente de la plataforma, pero en el código de producción se utilizan muchas construcciones de C ++ no portátiles. No veo ninguna razón para prohibirlo.Un constructor es una función que se llama cuando el objeto aún no existe, por lo que no podría ser una función miembro. Podría ser estático.
En realidad, se llama a un constructor con este puntero, después de que se haya asignado la memoria pero antes de que se haya inicializado por completo. Como consecuencia, un constructor tiene una serie de características privilegiadas.
Si tuviera un puntero a un constructor, tendría que ser un puntero estático, algo así como una función de fábrica o un puntero especial a algo que se llamaría inmediatamente después de la asignación de memoria. No podría ser una función miembro ordinaria y seguir funcionando como constructor.
El único propósito útil que viene a la mente es un tipo especial de puntero que podría pasarse al nuevo operador para permitirle indirectamente sobre qué constructor usar. Supongo que eso podría ser útil, pero requeriría una nueva sintaxis significativa y presumiblemente la respuesta es: lo pensaron y no valió la pena el esfuerzo.
Si solo desea refactorizar un código de inicialización común, una función de memoria ordinaria suele ser una respuesta suficiente, y puede obtener un puntero a uno de esos.
fuente
&A::A
no funciona en cualquiera de los compiladores que he probado.)Esto se debe a que no es un tipo de constructor de retorno y no está reservando ningún espacio para el constructor en la memoria. Como lo hace en caso de variable durante la declaración. Por ejemplo: si escribe una variable simple X Entonces el compilador generará un error porque el compilador no entenderá el significado de esto. Pero cuando escribes Int x; Luego, el compilador se da cuenta de que int escribe la variable de datos, por lo que reservará algo de espacio para la variable.
Conclusión: la conclusión es que, debido a la exclusión del tipo de retorno, no obtendrá la dirección en la memoria.
fuente
(void)(*fptr)()
declara un puntero a una función sin valor de retorno.Voy a adivinar:
El constructor y destructor de C ++ no son funciones en absoluto: son macros. Se insertan en el alcance donde se crea el objeto y el alcance donde se destruye el objeto. A su vez, no hay constructor ni destructor, el objeto simplemente ES.
En realidad, creo que las otras funciones en la clase tampoco son funciones, sino funciones en línea que NO se insertan porque tomas la dirección de ellas (el compilador se da cuenta de que estás en él y no inserta o inserta el código en la función y optimiza esa función) y, a su vez, la función parece "seguir estando allí", aunque no lo haría si no se hubiera tomado la dirección.
La tabla virtual del "objeto" C ++ no es como un objeto JavaScript, donde puede obtener su 'constructor y crear objetos a partir de él en tiempo de ejecución
new XMLHttpRequest.constructor
, sino más bien una colección de punteros a funciones anónimas que actúan como medios para interactuar con este objeto , excluyendo la capacidad de crear el objeto. Y ni siquiera tiene sentido "eliminar" el objeto, porque es como tratar de eliminar una estructura, no puedes: es solo una etiqueta de pila, solo escríbela como quieras debajo de otra etiqueta: eres libre de usa una clase como 4 enteros:No hay pérdida de memoria, no hay problemas, excepto que efectivamente desperdició un montón de espacio de pila reservado para la interfaz de objetos y la cadena, pero no va a destruir su programa (siempre y cuando no intente usarlo como una cuerda nunca más).
En realidad, si mis suposiciones anteriores son correctas: el costo completo de la cadena es solo el costo de almacenar estos 32 bytes y el espacio constante de la cadena: las funciones solo se usan en el momento de la compilación, y también pueden alinearse y desecharse después el objeto se crea y se usa (como si estuviera trabajando con una estructura y solo se refiriera a él directamente sin llamadas a funciones, asegúrese de que haya llamadas duplicadas en lugar de saltos de funciones, pero esto generalmente es más rápido y usa menos espacio). En esencia, cada vez que llama a cualquier función, el compilador simplemente reemplaza esa llamada con las instrucciones para hacerlo literalmente, con las excepciones que los diseñadores de idiomas han establecido.
Resumen: los objetos C ++ no tienen idea de lo que son; Todas las herramientas para interactuar con ellas están alineadas estáticamente y se pierden en tiempo de ejecución. Esto hace que trabajar con clases sea tan eficiente como llenar estructuras con datos y trabajar directamente con esos datos sin llamar a ninguna función (estas funciones están en línea).
Esto es completamente diferente de los enfoques de COM / ObjectiveC y javascript, que retienen la información de tipo dinámicamente, a costa de tiempo de ejecución, administración de memoria, llamadas de construcciones, ya que el compilador no puede tirar esta información: es necesario para despacho dinámico. Esto a su vez nos da la capacidad de "Hablar" con nuestro programa en tiempo de ejecución y desarrollarlo mientras se ejecuta al tener componentes reflectantes.
fuente
struct { int size; const char * data; };
(como parece suponer), escribe 4 * 4 bytes = 16 bytes en una dirección de memoria donde solo reservó 8 bytes en una máquina x86, por lo que 8 bytes se escriben sobre otros datos (que pueden dañar su pila ) Afortunadamente,std::string
normalmente tiene algo de optimización en el lugar para cadenas cortas, por lo que debe ser lo suficientemente grande para su ejemplo cuando se usa alguna implementación estándar.