Modificar const
variables o métodos para decirle al compilador que son inmutables ayuda al compilador a optimizar el código y ayuda a los desarrolladores a comprender si la función tiene efectos secundarios. Además, el uso const &
evita que el compilador copie datos innecesarios. Vale la pena leer el comentario de John Carmack sobre ```const```[2].
// Bad Idea
class MyClass
{
public:
void do_something(int i);
void do_something(std::string str);
};
// Good Idea
class MyClass
{
public:
void do_something(const int i);
void do_something(const std::string &str);
};
Piense detenidamente en los tipos de devolución
-
Getters (API de lectura de variables miembro)
-
En circunstancias normales, al leer variables miembro a través de valores de retorno, el uso
&
deconst &
valores de retorno puede mejorar significativamente el rendimiento. -
Devolver por valor es más propicio para la seguridad de los subprocesos. Si el valor devuelto es para copiar y usar, no habrá pérdida de rendimiento.
-
Si el valor de retorno de la API utiliza tipos de retorno covariantes, debe devolver
&
o*
-
-
Valores temporales y locales.
-
Siempre devuelto por valor
-
No utilice referencias constantes para pasar y devolver tipos simples
// Very Bad Idea
class MyClass
{
public:
explicit MyClass(const int& t_int_value)
: m_int_value(t_int_value)
{
}
const int& get_int_value() const
{
return m_int_value;
}
private:
int m_int_value;
}
En cambio, los tipos simples se pasan y devuelven por valor. Si no planeas cambiar los valores pasados, declaralos como const
, pero no como const
referencias:
// Good Idea
class MyClass
{
public:
explicit MyClass(const int t_int_value)
: m_int_value(t_int_value)
{
}
int get_int_value() const
{
return m_int_value;
}
private:
int m_int_value;
}
¿Por qué esto es tan? Debido a que pasar y regresar por referencia da como resultado operaciones de puntero, el paso por valor se maneja en los registros del procesador y es más rápido.
Evite acceder a la memoria sin procesar
Es difícil en C++ manejar correctamente el acceso, la asignación y la desasignación de la memoria sin procesar sin el riesgo de errores y fugas de memoria [3], y C++ 11 proporciona herramientas para evitar estos problemas.
// Bad Idea
MyClass *myobj = new MyClass;
// ...
delete myobj;
// Good Idea
auto myobj = std::make_unique<MyClass>(constructor_param1, constructor_param2); // C++14
auto myobj = std::unique_ptr<MyClass>(new MyClass(constructor_param1, constructor_param2)); // C++11
auto mybuffer = std::make_unique<char[]>(length); // C++14
auto mybuffer = std::unique_ptr<char[]>(new char[length]); // C++11
// or for reference counted objects
auto myobj = std::make_shared<MyClass>();
// ...
// myobj is automatically freed for you whenever it is no longer used.
Utilice std::array
o std::vector
en lugar de matrices estilo C
Ambos métodos garantizan un diseño de memoria contigua de los objetos y pueden (y deben) reemplazar completamente las matrices de estilo C, y son una de las muchas razones para no utilizar punteros sin formato.
Además, evite usar ```std::shared_ptr``` para guardar matrices[4].
Usar excepción
Los valores de retorno (por ejemplo boost::optional
) se pueden ignorar y pueden causar fallas o errores de memoria si no se verifican, mientras que las excepciones no se pueden ignorar. Las excepciones, por otro lado, pueden detectarse y gestionarse. Es posible que la excepción aumente al nivel más alto de la aplicación, donde será detectada, registrada en el registro y desencadenará un reinicio automático de la aplicación.
Stroustrup, uno de los diseñadores de C++, habló sobre este tema: ¿Por qué utilizar excepciones?[5]
Utilice conversiones de estilo C++ en lugar de conversiones de estilo C
Reemplace las conversiones de estilo C con conversiones de estilo C++ ( static_cast<>
,, dynamic_cast<>
...), que permiten más comprobaciones del compilador y son bastante seguras.
// Bad Idea
double x = getX();
int i = (int) x;
// Not a Bad Idea
int i = static_cast<int>(x);
Además, el estilo de conversión de tipos de C++ es más explícito y más fácil de buscar.
Pero si necesita double
convertir tipos en int
tipos, considere refactorizar la lógica de su programa (por ejemplo, comprobaciones adicionales de desbordamiento y subdesbordamiento). Evite la situación de medir 3 veces y luego cortar 0,9999999999981 veces.
No definir funciones variadas
Una función variada puede aceptar un número variable de argumentos, quizás el ejemplo más famoso sea printf()
. Aunque es posible definir dichas funciones, puede haber riesgos de seguridad. El uso de funciones variadas no es seguro para los tipos y los parámetros de entrada incorrectos pueden hacer que el programa finalice con un comportamiento indefinido. Este comportamiento indefinido puede causar problemas de seguridad. Si usa un compilador que admita C++1, puede usar plantillas variadas.