2009-10-31 13 views
6

He decidido familiarizarme más con mi lenguaje de programación favorito, pero solo leyendo el estándar es aburrido.¿Cuáles son los elementos más sorprendentes del estándar C++?

¿Cuáles son los elementos más sorprendentes, contrarios a la intuición o simplemente extraños de C++? ¿Qué te ha sorprendido lo suficiente como para haber corrido hacia tu compilador más cercano para comprobar si es realmente cierto?

Voy a aceptar la primera respuesta que no voy a creer, incluso después de Lo he probado. :)

+7

Debe ser wiki de la comunidad. – Brian

+1

Véase también http://stackoverflow.com/questions/75538/hidden-features-of-c –

+0

Gracias. No se me ocurrió buscar "funciones ocultas". –

Respuesta

8

El orden de varios declaradores es en realidad desordenada:

volatile long int const long extern unsigned x; 

es lo mismo que

extern const volatile unsigned long long int x; 
+2

¿Es realmente tan sorprendente? ¿Te importa explicar por qué te sorprendió? – akent

+9

Yo, por ejemplo, no sabía que se podía separar la parte "long long int". –

+0

En realidad, es ilegal que los especificadores de almacenamiento como "extern" aparezcan dentro del especificador de tipo. Deben aparecer antes o después. Su primer ejemplo no es C++ válido, pero lo sería si cambiara "extern" y "unsigned". –

5

Teniendo en cuenta lo implacable C++ suele ser, me pareció un poco sorprendente que la norma permite en realidad a delete punteros nulos.

+5

Me resulta sorprendente la cantidad de gente que programa en C++ que no sabe que puede eliminar punteros nulos. – bk1e

+0

Aprendí C antes de pasar a C++, así que aprendí a no hacer _ cualquier cosa_ con un puntero NULL. El hecho de que C++ define un no-operativo para 'eliminar NULL;' podría ser útil, pero también puede ocultar errores. –

+3

No hay sorpresa si se da cuenta de que en C 'libre (NULL)' está bien definido. – MSalters

9

Otra respuesta que podría agregar sería el calificador throw(). Por ejemplo:

void dosomething() throw() 
{ 
    // .... 
} 

void doSomethingElse() throw(std::exception) 
{ 
    // .... 
} 

Intuitivamente, esto parece como un contrato con el compilador, lo que indica que esta función no se le permite lanzar cualquier excepción a excepción de los enumerados. Pero en realidad, esto no hace nada en tiempo de compilación. Más bien es un mecanismo en tiempo de ejecución y no evitará que la función arroje una excepción. Peor aún, si se lanza una excepción no listada, termina su aplicación con una llamada al std::terminate().

+4

Herb Sutter explica con gran detalle por qué nunca debe escribir especificaciones de excepción: http://www.gotw.ca/publications/mill22.htm –

+0

Es exactamente por eso que utilizamos la declaración throw comentada, solo para fines de documentación ... bastante una pena:/ –

0

No es muy conocido que un iniciador de matriz puede saltar en el índice como en la definición de enumeración.

// initializing an array of int 
int a[ 7] = { [5]=1, [2]=3, 2}; 
// resulting in 
int a[ 7] = { 0, 0, 3, 2, 0, 1, 0}; 

// initializing an array of struct 
struct { int x,y; } ar[ 4] = { [1].x=23, [3].y=34, [1].y=-1, [1].x=12}; 
// resulting in 
struct { int x,y; } ar[ 4] = { { 0, 0}, { 12, -1}, { 0, 0}, { 0, 34}}; 

// interesting usage 
char forbidden[ 256] = { ['a']=1, ['e']=1, ['i']=1, ['o']=1, ['u']=1}; 

mayoría de these son ciertas para C++ también.

+5

-1, esta es una extensión de gcc. La pregunta menciona el estándar según el cual el fragmento anterior está mal formado. – avakar

+0

sí, esto es específico de gcc. tan increíble como es gcc, no es parte del estándar. –

10

me pareció algo sorprendente que

class aclass 
{ 
public: 
int a; 
}; 

some_function(aclass()); 

tendrá a inicializado a 0 en some_function, mientras

aclass ac; 
some_function(ac); 

lo dejará unitinitalized. Si define explícitamente el constructor por defecto de aclass:

class aclass 
{ 
public: 
aclass(): a() {} 
int a; 
}; 

continuación aclass ac; también inicializar a-0.

+0

¿qué? cuidado de explicar por qué es diferente? ¿Inicializa el espacio de pila empujado por el bien de una llamada? –

+6

Usando 'aclass()' como el parámetro hace que el compilador complete un constructer predeterminado que inicializa todo al valor predeterminado. Pero cuando solo declaras una variable, no se llama a ningún constructor a menos que tú mismo hayas definido un constructor predeterminado. – henle

+0

hmm, es verdad, lo probé: P. +1 por algo que no sabía. –

2

Objetos de tipo de cambio durante la construcción.

En particular, cuando se llama a una función virtual desde un constructor, se llamará a no llamando a la anulación más derivada. Por el contrario, está llamando a la implementación de la clase que está actualmente construyendo.Si esa implementación es 0 (función virtual pura), su programa se bloqueará en el tiempo de ejecución.

Un ejemplo semi-real-mundo:

class AbstractBase { 
    public: 
    AbstractBase() { 
     log << "Creating " << className() << endl; 
    } 
    protected: 
    virtual string className() const = 0; 
} 

class ConcreteGuy { 
    protected: 
    virtual string className() const { return "ConcreteGuy"; } 
} 

Al construir un objeto ConcreteGuy, el programa terminará con un mensaje de "llamar la función virtual pura" de error.

Es por eso que llamar a las funciones virtuales de los constructores se considera malo.

+3

no es sorprendente, cuando construyes un ConcreteGuy (asumiendo que se deriva de AbstractBase) como la clase base se construye primero, no tiene una clase ConcreteGuy para llamar hasta que el constructor esté completo. – gbjbaanb

+1

Tiene sentido, estoy de acuerdo. Pero aún causó bastante irritación cuando me topé por primera vez con esto. Especialmente porque Java y C# se comportan de manera diferente, permitiéndole llamar a métodos en objetos que no están completamente construidos. – Thomas

+1

@gbjbaanb (¡qué nombre!): En realidad, el compilador no coincidirá con la llamada de construcción con los constructores de tipos más derivados y comenzará a ejecutar ese constructor. Ahora, el orden de ejecución es primero la lista de inicialización, luego el cuerpo constructor y el estándar establece que la inicialización comienza a ejecutar la primera clase base de la clase actual. El algoritmo implica que el primer tipo que está completamente construido (tanto la lista de inicialización como el cuerpo constructor compitieron) es el tipo menos derivado (por alguna definición de él) ... –

0

En C++ declaraciones evaluar a algo ...

int main() 
{ 
    "This is a valid C++ program!" 
    "I will list the first five primes:"; 
    2; 
    3; 
    5; 
    7; 
    11; 
} 
+0

No es un programa válido sin una declaración de devolución. –

+4

En realidad, en C++, 'main' no necesita devolver un valor explícitamente. –

+1

@Jurily ¡Lo es! verifique los estándares :) – AraK

Cuestiones relacionadas