2013-05-07 1 views
15

Es ist keine gute Übung, STL-Klassen in der DLL-Schnittstelle zu verwenden, wie Common practice in dealing with warning c4251: class … needs to have dll-interface erklärt. Ein Beispiel dafür ist gegeben:Eine Möglichkeit, die Warnung C4251 bei Verwendung von STL-Klassen in der DLL-Schnittstelle zu beseitigen

#include <iostream> 
#include <string> 
#include <vector> 


class __declspec(dllexport) HelloWorld 
{ 
public: 
    HelloWorld() 
    { 
     abc.resize(5); 
     for(int i=0; i<5; i++) 
      abc[i] = i*10; 

     str="hello the world"; 
    } 
    ~HelloWorld() 
    { 

    } 

    std::vector<int> abc; 
    std::string str; 

}; 

Wenn diese Datei zu kompilieren, können die folgenden Warnhinweise zu beachten:

warning C4251: 'HelloWorld::str' : class 'std::basic_string<_Elem,_Traits,_Ax>' needs to have dll-interface to be used by clients of class 'HelloWorld'  
warning C4251: 'HelloWorld::abc' : class 'std::vector<_Ty>' needs to have dll-interface to be used by clients of class 'HelloWorld' 

Dann ist die Frage, wie wir die gleiche Funktionalität Vektor und String ohne STL-Klasse implementieren kann. Eine Implementierung ich denken konnte, ist wie folgt:

class __declspec(dllexport) HelloWorld2 
{ 
public: 
    HelloWorld2() 
    { 
     abc_len = 5; 
     p_abc = new int [abc_len]; 
     for(int i=0; i<abc_len; i++) 
      p_abc[i] = i*10; 

     std::string temp_str("hello_the_world"); 
     str_len = temp_str.size(); 
     p_str = new char[str_len+1]; 
     strcpy(p_str,temp_str.c_str()); 
    } 
    ~HelloWorld2() 
    { 
     delete []p_abc; 
     delete []p_str; 
    } 

    int *p_abc; 
    int abc_len; 
    char *p_str; 
    int str_len; 



}; 

Wie Sie sehen können, in der neuen Implementierung wir int * p_abc verwenden Vektor abc und char * z_str zu ersetzen String str zu ersetzen. Die Frage, die ich habe, ist, ob es andere elegante Implementierungsansätze gibt, die dasselbe tun können. Vielen Dank!

Antwort

19

Ich glaube, Sie mindestens drei mögliche Optionen haben, dieses Problem zu lösen:

  • Sie könnten noch STL/Template-Klassen für Ihre Felder halten und sie auch als Parametertypen verwenden, solange Sie halte alle Felder privat (was sowieso eine gute Praxis ist). Here ist eine Diskussion darüber. Um die Warnungen zu entfernen, können Sie einfach #pragma Anweisungen verwenden.

  • Sie könnten kleine Wrapper-Klassen mit privaten STL-Feldern erstellen und Felder Ihrer Wrapper-Klassen-Typen stattdessen der Öffentlichkeit zugänglich machen, aber dies würde höchstwahrscheinlich nur die Warnungen an andere Stellen verschieben.

  • Sie können das PIMPL Idiom verwenden, um Ihre STL-Felder in einer privaten Implementierungsklasse zu verbergen, die nicht einmal aus Ihrer Bibliothek exportiert wird oder außerhalb davon sichtbar ist.

  • Oder Sie könnten eigentlich alle erforderlichen Template-Klasse Spezialisierungen exportieren, wie sie in der C4251 Warnung vorgeschlagen, in einer Weise, die here beschrieben. Kurz gesagt würden Sie folgende Codezeilen vor Ihrer HelloWorld Klasse einzufügen haben:

    ... 
    #include <vector> 
    
    template class __declspec(dllexport) std::allocator<int>; 
    template class __declspec(dllexport) std::vector<int>; 
    template class __declspec(dllexport) std::string; 
    
    class __declspec(dllexport) HelloWorld 
    ... 
    

    By the way, scheint die Reihenfolge dieser Ausfuhren wichtig: die Vektor-Klasse-Vorlage die allocator Klassenvorlage intern verwendet, so dass die Zuweisungsinstanziierung muss vor der Vektor Instanziierung exportiert werden.

  • Die direkte Nutzung von intrinsics ist wahrscheinlich die schlechteste Option, aber wenn Sie Ihre Felder

    int *p_abc; 
    int abc_len; 
    

    in so etwas wie class MyFancyDataArray und die Felder kapseln

    char *p_str; 
    int str_len; 
    

    in so etwas wie class MyFancyString, dann diese wäre eine anständige Lösung (ähnlich der im zweiten Punkt beschriebenen). Aber es ist wahrscheinlich nicht die beste Angewohnheit, das Rad zu oft neu zu erfinden.

+0

Ich versuchte Ihren vierten Vorschlag, aber ich habe den C2242 Fehler, wenn sie mit VS2010 kompilieren. – Simon

+0

Es ist schwer zu sagen, warum der Compiler mit C2242 klagt, ohne den Code oder zumindest die volle Fehlermeldung zu sehen, aber ich würde vermuten, dass irgendwo vor einem Typedef ein Semikolon fehlt. Vielleicht wurde die Zeile 'class __declspec (dllexport) HelloWorld' versehentlich kopiert? – buygrush

+2

@Simon: verwenden Sie 'Vorlagenklasse __declspec (dllexport) Std :: basic_string ;' –

5

OR tun, um die einfachste Sache zu tun, bewegen Sie die __declspec die nur Mitglieder Sie exportieren Pflege:

class HelloWorld 
{ 
public: 
    __declspec(dllexport) HelloWorld() 
    { 
     abc.resize(5); 
     for(int i=0; i<5; i++) 
      abc[i] = i*10; 

     str="hello the world"; 
    } 
    __declspec(dllexport) ~HelloWorld() 
    { 
    } 
    std::vector<int> abc; 
    std::string str; 
}; 
+1

Funktioniert das Exportieren nur einiger Methoden, aber nicht der ganzen Klasse? – jpo38

+0

Es tut, wenn Sie die Klasse exportieren, was der Compiler tut, ist Export alle Mitglieder der Klasse, gibt es wirklich keine ‚Klasse‘ Symbol in der binären, aber es gibt eine (wenn auch verstümmelt) Hello World :: Helloworld() und Hello World: : ~ HelloWorld() in diesem speziellen Fall. Da Sie die Datenelemente nicht direkt zugreifst, gibt es keine Notwendigkeit, sie zu exportieren, aber der Compiler muss noch ihre Erklärung, um zu sehen, die Größe einer Instanz der Klasse, um herauszufinden. Making dann privat ist immer eine gute Praxis. –

+1

Beim Exportieren einzelner Methoden aus einer DLL müssen Sie den Zugriff auf die nicht exportierten Elemente kontrollieren, da der Compiler (für die Exe) den entsprechenden Code auf der "anderen Seite" der DLL erzeugen und aufrufen wird die exportierten Funktionen von der DLL. Die Zugriffssteuerung muss auf Membervariablen und spezielle Member (wie Kopieren und Zuweisung) erweitert werden. Am besten ist es, wenn die nicht exportierten Mitglieder alle privat sind (oder '= delete'). – Niall

3

Ich bin nicht sicher, welches Problem Sie hier lösen wollen. Es gibt zwei verschiedene Probleme: Cross-Compiler-Binärkompatibilität und Vermeidung von Link-Fehlern "undefined Symbol". Die C4251-Warnung, die Sie zitiert haben, spricht über das zweite Problem. Was ist wirklich ein Nicht-Problem, vor allem mit SCL-Klassen wie std::string und std::vector. Da sie in der CRT implementiert werden, solange beide Seiten Ihrer Anwendung die gleiche CRT verwenden, wird alles "nur funktionieren". Die Lösung ist in diesem Fall, nur die Warnung zu deaktivieren.

Cross-Compiler-Binärkompatibilität OTOH, was in der anderen Stackoverflow-Frage besprochen wird, ist ein ganz anderes Biest. Damit das funktioniert, müssen Sie im Grunde alle Ihre öffentlichen Header-Dateien frei von jeglicher Erwähnung irgendeiner SCL-Klasse halten. Das heißt, Sie müssen entweder alles PIMPL oder abstrakte Klassen überall verwenden (oder eine Mischung daraus).

Verwandte Themen