[Notas de aprendizagem da linguagem C]: Segurança

Modificar constvariáveis ​​ou métodos para informar ao compilador que eles são imutáveis ​​ajuda o compilador a otimizar o código e ajuda os desenvolvedores a entender se a função tem efeitos colaterais. Além disso, using const &evita que o compilador copie dados desnecessários. Vale a pena ler o comentário 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);
};

Pense cuidadosamente sobre os tipos de retorno

  • Getters (API de leitura de variável de membro)

    • Em circunstâncias normais, ao ler variáveis ​​de membro por meio de valores de retorno, usar &ou const &retornar valores pode melhorar significativamente o desempenho.

    • Retornar por valor é mais propício à segurança do thread. Se o valor retornado for para cópia e uso, não haverá perda de desempenho.

    • Se o valor de retorno da API usar tipos de retorno covariantes, ele deverá retornar &ou*

  • Valores temporários e locais

    • Sempre retornado por valor

Não use referências const para passar e retornar 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;
}

Em vez disso, tipos simples são passados ​​e retornados por valor. Se você não planeja alterar os valores passados, declare-os como const, mas não como constreferências:

// 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;
}

Porque isto é assim? Como a passagem e o retorno por referência resultam em operações de ponteiro, a passagem por valor é tratada nos registradores do processador e é mais rápida.

Evite acessar a memória bruta

É difícil em C++ lidar corretamente com acesso, alocação e desalocação de memória bruta sem o risco de erros e vazamentos de memória [3], e C++ 11 fornece ferramentas para evitar esses 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.

Use std::arrayou std::vectorem vez de matrizes estilo C

Ambos os métodos garantem um layout de memória contíguo de objetos e podem (e devem) substituir completamente os arrays no estilo C, e são um dos muitos motivos para não usar ponteiros brutos.

Além disso, evite usar ```std::shared_ptr``` para salvar arrays[4].

Usar exceção

Os valores de retorno (por exemplo boost::optional), podem ser ignorados e podem causar travamentos ou erros de memória se não forem verificados, enquanto as exceções não podem ser ignoradas. As exceções, por outro lado, podem ser capturadas e tratadas. É possível que a exceção suba até o nível mais alto da aplicação, onde será capturada, registrada no log e acione o reinício automático da aplicação.

Stroustrup, um dos designers de C++, falou sobre este tópico: Por que usar exceções?[5]

Use conversões no estilo C++ em vez de conversões no estilo C

Substitua as conversões no estilo C por conversões no estilo C++ ( static_cast<>, dynamic_cast<>, ...), que permitem mais verificações do compilador e são bastante seguras.

// Bad Idea
double x = getX();
int i = (int) x;

// Not a Bad Idea
int i = static_cast<int>(x);

Além disso, o estilo de conversão de tipo C++ é mais explícito e mais fácil de pesquisar.

Mas se você precisar doubleconverter tipos em inttipos, considere refatorar a lógica do seu programa (por exemplo, verificações adicionais de overflow e underflow). Evite a situação de medir 3 vezes e depois cortar 0,9999999999981 vezes.

Não defina funções variáveis

Uma função variada pode aceitar um número variável de argumentos, sendo talvez o exemplo mais famoso printf(). Embora seja possível definir tais funções, pode haver riscos de segurança. O uso de funções variadas não é seguro para tipos e parâmetros de entrada incorretos podem fazer com que o programa termine com comportamento indefinido. Este comportamento indefinido pode causar problemas de segurança. Se você usar um compilador que suporte C++1, poderá usar modelos variados.

Acho que você gosta

Origin blog.csdn.net/Jiangziyadizi/article/details/129372662
Recomendado
Clasificación