Bereinigen von C/C++-Include-Anweisungen in Visual Studio

Ab Visual Studio 17.8 Preview 1 bietet Visual Studio ein #include-Bereinigungsfeature, das die Qualität Ihres Codes auf folgende Weise verbessert:

  • Bietet das Hinzufügen von Headerdateien für Code, der nur kompiliert wird, weil eine erforderliche Headerdatei indirekt von einer anderen Headerdatei eingeschlossen wird.
  • Bietet an, nicht verwendete Headerdateien zu entfernen, um Buildzeiten und Codeübersichtlichkeit zu verbessern.

Die Include-Bereinigung ist standardmäßig aktiviert. Informationen zum Konfigurieren finden Sie unter Konfigurieren der C/C++-Include-Bereinigung in Visual Studio.

Direkte und indirekte Header

Zuerst einige Informationen zur Terminologie:

  • Ein direkter Header ist ein Header, den Sie mit #include explizit in den Code aufnehmen.
  • Ein indirekter Header ist ein Header, den Sie nicht explizit mit #include aufnehmen. Stattdessen ist er in einer Headerdatei enthalten, die Sie direkt einschließen. Wir sagen auch, dass ein indirekter Header transitiv (transitively) eingeschlossen wird.

Die Include-Bereinigung analysiert Ihren Code und bestimmt, welche Header nicht verwendet werden und welche indirekt eingeschlossen werden. Betrachten Sie die folgende Headerdatei:

// myHeader.h

#include <string>
#include <iostream>

void myFunc()
{
    std::string s = "myFunc()\n";
    std::cout << s;
}

Und das Programm, das sie verwendet:

// myProgram.cpp
#include "myHeader.h"

int main()
{
    std::string s = "main()"; // string is indirectly included by myHeader.h
    std::cout << s; // cout is indirectly included by myHeader.h
    myFunc();
}

myHeader.h ist ein direkter Header, da myProgram.cpp ihn explizit einschließt. myHeader.h enthält <string> und <iostream>, sodass es sich um indirekte Header handelt.

Das Problem besteht darin, dass myProgram.cppstd::string und std::cout verwendet, aber nicht direkt die Header einschließt, die sie definieren. Dieser Code wird kompiliert, da myHeader.h diese Header enthält. Dieser Code ist fehleranfällig, denn wenn myHeader.h einen der beiden nicht mehr enthält, wird myProgram.cpp nicht mehr kompiliert.

Gemäß den C++-Richtlinien ist es besser, Header für alle Abhängigkeiten explizit einzuschließen, damit Ihr Code nicht der Fehleranfälligkeit unterliegt, die durch Änderungen an Headerdateien verursacht wird. Weitere Informationen finden Sie unter C++ Core Guidelines SF.10.

Die Include-Bereinigung analysiert Ihren Code, um nicht verwendete und indirekt eingeschlossene Header zu ermitteln. Sie erhalten Feedback basierend auf den Einstellungen, die unter Konfigurieren der C/C++-Include-Bereinigung in Visual Studio beschrieben sind. Feedback kann in Form von Fehlerlistenwarnungen, Vorschlägen usw. zurückgegeben werden. Weitere Informationen zu dem von der Include-Bereinigung zurückgegebenen Feedback finden Sie unter Meldungen der Include-Bereinigung.

Nicht verwendete Header

Da sich Ihr Code weiterentwickelt, benötigen Sie möglicherweise einige Headerdateien nicht mehr. Bei einem komplexen Projekt ist es schwer, den Überblick zu behalten. Im Laufe der Zeit dauert die Ausführung Ihrer Builds möglicherweise länger, da der Compiler unnötige Headerdateien verarbeitet. Mit der Include-Bereinigung können Sie nicht verwendete Header finden und entfernen. Beispiel für die Auskommentierung von myFunc() in myProgram.cpp:

// myProgram.cpp
#include "myHeader.h"

int main()
{
    std::string s = "main()"; // string is indirectly included from myHeader.h
    std::cout << s; // cout is indirectly included from myHeader.h
    // myFunc(); // directly included from myHeader.h
}

Im folgenden Screenshot ist #include "myHeader.h" abgeblendet (eine unter Konfigurieren der C/C++-Include-Bereinigung in Visual Studio beschriebenen Einstellung), da sie nicht verwendet wird, weil myFunc() auskommentiert ist.

Zeigen Sie mit dem Cursor auf die abgeblendete #include-Option, um das Menü „Schnelle Aktion“ anzuzeigen. Klicken Sie auf die Glühbirne (oder wählen Sie den Link Potenzielle Fehlerbehebungen anzeigen aus), um Aktionen im Zusammenhang mit der nicht verwendeten Datei anzuzeigen:

Three refactoring options are shown: Remove # include myHeader.h, remove all unused includes, and Add all transitively used and remove all unused # includes.

Hinzufügen von transitiv verwendeten Headern

Wir könnten die nicht verwendete Headerdatei entfernen, aber dadurch wird der Code beschädigt, da <string> und <iostream> indirekt über myheader.h eingeschlossen werden.

Stattdessen können wir Alle transitiv verwendeten #include-Anweisungen hinzufügen und alle nicht verwendeten #include-Anweisungen entfernen auswählen. Dadurch wird der nicht verwendete Header myHeader.h entfernt, aber es werden auch alle verwendeten Header hinzugefügt, die indirekt über myHeader.h eingeschlossen werden. Das Ergebnis ist in diesem Fall das Hinzufügen von #include <string> und #include <iostream> zu myProgram.cpp und das Entfernen von #include "myHeader.h":

// myProgram.cpp
#include <iostream>
#include <string>

int main()
{
    std::string s = "main()"; // string is directly included from <string>
    std::cout << s; // cout is directly included from <string>
    // MyFunc();
}

Das Tool aktualisiert die Kommentare nicht, aber Sie können sehen, dass der Code jetzt std::string und std::cout direkt verwendet. Dieser Code ist nicht mehr fehleranfällig, da er nicht davon abhängt, dass myHeader.h die anderen erforderlichen Header einschließt.

Bewährte Vorgehensweise

Entfernen Sie scheinbar nicht verwendete Headerdateien nicht, ohne zuerst indirekt eingeschlossene Headerdateien hinzuzufügen. Der Grund dafür ist, dass Ihr Code möglicherweise von indirekten include-Anweisungen in einer Headerdatei abhängig ist, die andernfalls nicht verwendet wird. Fügen Sie zuerst transitiv verwendete Header hinzu. Wenn Sie dann nicht verwendete Header entfernen, erhalten Sie keine Kompilierungsfehler aufgrund fehlender Headerdateien, die indirekt von einer von Ihnen entfernten Headerdatei eingeschlossen wird.

Eine Möglichkeit hierzu besteht darin, die Einstellung der Include-Bereinigung für Vorschlagsebene für Hinzufügen fehlender include-Anweisungen auf Vorschlag festzulegen (Extras>Optionen>Text-Editor>C/C++>Codebereinigung). Legen Sie außerdem Vorschlagsebene für Entfernen nicht verwendeter include-Anweisungen auf Vorschlag fest. Führen Sie dann folgende Schritte aus:

  1. Stellen Sie in der Fehlerliste sicher, dass der Filter auf Build und IntelliSense festgelegt ist.
  2. Suchen Sie nach Instanzen von „Inhalt aus #include x wird in dieser Datei verwendet und transitiv eingeschlossen“.
  3. Zeigen Sie mit dem Cursor auf eine Zeile mit dem Vorschlag. Wählen Sie im Dropdownmenü mit der Glühbirne Alle transitiv verwendeten include-Anweisungen hinzufügen aus.
  4. Wiederholen Sie diese Schritte in Ihrem Projekt, bis alle Vorschläge zu transitiven include-Anweisungen behandelt wurden.
  5. Nicht verwendete Elemente entfernen: Suchen Sie in der Fehlerliste nach einer Instanz von „#include x wird in dieser Datei nicht verwendet.“.
  6. Zeigen Sie mit dem Cursor auf den nicht verwendeten Header. Wählen Sie im Dropdownmenü mit der Glühbirne Alle nicht verwendeten include-Anweisungen entfernen aus.
  7. Wiederholen Sie diese Schritte in Ihrem Projekt, bis alle Vorschläge der Include-Bereinigung behandelt wurden.

In dieser kurzen Übersicht haben Sie gesehen, wie die Include-Bereinigung Ihnen helfen kann, nicht verwendete Header zu entfernen und Header hinzuzufügen, die indirekt eingeschlossen wurden. Auf diese Weise bleibt Ihr Code übersichtlich und kann möglicherweise schneller erstellt werden, und die Fehleranfälligkeit Ihres Codes wird verringert.

Siehe auch

Konfigurieren der C/C++-Include-Bereinigung in Visual Studio
Meldungen der Include-Bereinigung