[Notes d'apprentissage du langage C] : Sécurité

La modification constde variables ou de méthodes pour indiquer au compilateur qu'elles sont immuables aide le compilateur à optimiser le code et aide les développeurs à comprendre si la fonction a des effets secondaires. De plus, l'utilisation const &empêche le compilateur de copier des données inutiles. Le commentaire de John Carmack sur ```const```[2] mérite d'être lu.

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

Réfléchissez bien aux types de retour

  • Getters (API de lecture de variables membres)

    • Dans des circonstances normales, lors de la lecture des variables membres via les valeurs de retour, l'utilisation &ou const &les valeurs de retour peuvent améliorer considérablement les performances.

    • Le retour par valeur est plus propice à la sécurité des threads.Si la valeur renvoyée est destinée à la copie et à l'utilisation, il n'y aura aucune perte de performances.

    • Si la valeur de retour de l'API utilise des types de retour covariants, elle doit renvoyer &ou*

  • Valeurs temporaires et locales

    • Toujours renvoyé par valeur

N'utilisez pas de références const pour transmettre et renvoyer des types 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;
}

Au lieu de cela, les types simples sont transmis et renvoyés par valeur. Si vous ne prévoyez pas de modifier les valeurs transmises, déclarez-les comme const, mais pas comme constréférences :

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

Pourquoi cela est-il ainsi? Étant donné que le passage et le retour par référence entraînent des opérations de pointeur, le passage par valeur est géré dans les registres du processeur et est plus rapide.

Évitez d'accéder à la mémoire brute

Il est difficile en C++ de gérer correctement l'accès, l'allocation et la désallocation de la mémoire brute sans risque d'erreurs et de fuites de mémoire [3], et C++11 fournit des outils pour éviter ces problèmes.

// 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.

Utiliser std::arrayou std::vectorà la place des tableaux de style C

Les deux méthodes garantissent une disposition de mémoire contiguë des objets et peuvent (et devraient) remplacer complètement les tableaux de style C, et sont l'une des nombreuses raisons de ne pas utiliser de pointeurs bruts.

Évitez également d'utiliser ```std::shared_ptr``` pour sauvegarder les tableaux[4].

Utiliser une exception

Les valeurs de retour (par exemple boost::optional) peuvent être ignorées et peuvent provoquer des plantages ou des erreurs de mémoire si elles ne sont pas cochées, alors que les exceptions ne peuvent pas être ignorées. Les exceptions, en revanche, peuvent être détectées et traitées. Il est possible que l'exception remonte jusqu'au niveau le plus élevé de l'application, où elle sera interceptée, enregistrée dans le journal et déclenchera un redémarrage automatique de l'application.

Stroustrup, l'un des concepteurs du C++, a parlé de ce sujet : Pourquoi utiliser des exceptions ?[5]

Utiliser des conversions de style C++ au lieu de conversions de style C

Remplacez les transtypages de style C par des transtypages de style C++ ( static_cast<>, dynamic_cast<>, ...), qui permettent davantage de vérifications du compilateur et sont assez sûrs.

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

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

De plus, le style de conversion de type C++ est plus explicite et plus convivial pour la recherche.

Mais si vous avez besoin de doubleconvertir des types en inttypes, envisagez de refactoriser la logique de votre programme (par exemple, des vérifications supplémentaires en cas de débordement et de sous-dépassement). Évitez la situation consistant à mesurer 3 fois puis à couper 0,9999999999981 fois.

Ne pas définir de fonctions variadiques

Une fonction variadique peut accepter un nombre variable d'arguments, l'exemple le plus célèbre étant peut-être printf(). Bien qu'il soit possible de définir de telles fonctions, des risques de sécurité peuvent exister. L'utilisation de fonctions variadiques n'est pas de type sécurisé et des paramètres d'entrée incorrects peuvent entraîner la fin du programme avec un comportement indéfini. Ce comportement non défini peut entraîner des problèmes de sécurité. Si vous utilisez un compilateur prenant en charge C++1, vous pouvez utiliser des modèles variadiques.

Je suppose que tu aimes

Origine blog.csdn.net/Jiangziyadizi/article/details/129372662
conseillé
Classement