2017-02-18 2 views
1

Ich habe ein wenig Verwirrung über den folgenden Fall. Ich habe die folgende C++ Klasse, die ein RGB-Pixel:Member-Objekt der C++ - Klasse: Wo sollte es zerstört werden?

class RgbColorVector 
    { 
     cv::Mat pM; 

    public: 
    RgbColorVector(unsigned int r,unsigned int g,unsigned int b) 
    { 
     pM = cv::Mat(3,1,CV_8UC1); 
     pM.at<unsigned char>(0,0) = r; 
     pM.at<unsigned char>(1,0) = g; 
     pM.at<unsigned char>(2,0) = b; 
} 


unsigned int getComponent(int c) 
{ 
    return (unsigned int)pM.at<unsigned char>(c,0); 
} 

Hier bin ich mit dem OpenCV Mat Objekt einen RGB-Wert zu halten. Normalerweise würde ich in solchen Fällen den cv :: Mat-Zeiger verwenden und den Heap-Speicher im Konstruktor mit einem neuen Operator belegen und ihn dann im Destruktor mit delete freigeben. Im obigen Fall rufe ich explizit den Konstruktor von cv :: Mat auf. Was ich erwartete war, dass wenn der Konstruktor von RgbColorVector beendet wird, der zugewiesene Speicher im cv :: Mat-Konstruktor zerstört würde. Aber anscheinend ist es nicht der Fall:

void func() 
{ 
    RgbColorVector rgbVec(105,42,45); 
    int g = rgbVec.getComponent(1); 
    std::cout<<g<<std::endl; 
} 


int main(int argc, const char * argv[]) { 
    func(); 
    return 0; 
} 

Hier erhalte ich als Ausgabe 42. Ich bin mir bewusst, dass „rgbVec“ ist eine lokale (automatische) Variable und wenn „Func“ beendet wird, dessen Inhalt (Klassenmitglieder) wird zerstört werden. Darüber hinaus erwartete ich, dass der getComponent() -Aufruf einen zufälligen Wert zurückgeben würde, da jeder im cv :: Mat-Konstruktoraufruf zugewiesene Speicher zerstört wird, nachdem der Konstruktor von "rgbVec" zurückgegeben wurde. Ich bin hier ein wenig verwirrt, wie genau funktioniert der Speicherzuweisungsmechanismus in diesem Fall? Ist das cv :: Mat-Objekt im RgbColorVector() -Konstruktor nicht im Stapel zugeordnet?

+1

Ich verstehe nicht, was für ein Verhalten man erwartet. Sie verwenden den Copy-Konstruktor eines cv :: Mat von pM = cv :: Mat (3,1, CV_8UC1); Das konstruierte Objekt befindet sich also in Ihrer Klassenmitgliedsvariablen (wobei es zerstört wird, wenn das Klassenobjekt zerstört wird), das sich am Ende von func() befindet. Wenn Sie tatsächlich auf freigegebenen Speicher zugreifen würden, werden keine "zufälligen" Werte zurückgegeben, sondern die Werte, die sich im Speicher befinden. Wenn der Speicher immer noch zu Ihrem Programm gehört und nicht überschrieben wurde, können Sie sogar die "richtigen" Werte beobachten, wenn Sie versehentlich den Speicher freigegeben haben. – Micka

+1

Sehr schlechte Idee, Mat zu verwenden, um einen einzelnen Pixelwert zu speichern. Sie sollten besser Vec3b verwenden oder einfach 3 uchars verwenden – Miki

Antwort

4
class RgbColorVector 
{ 
    cv::Mat pM; // <- this object is valid as long 
       // as the parent is (value semantics) 

public: 
    RgbColorVector(unsigned int r,unsigned int g,unsigned int b) 
     // <- pM gets implicitly allocated here 
    { 
     pM = cv::Mat(3,1,CV_8UC1); // <- this creates a temporary, and 
            // assign its value to pM 

     // <- the temporary gets implicitly deallocated here. 
     // pM isn't affected. 

     pM.at<unsigned char>(0,0) = r; 
     pM.at<unsigned char>(1,0) = g; 
     pM.at<unsigned char>(2,0) = b; 
    } 
}; 

Der richtige Weg, ein Mitglied zu initialisieren ist wie folgt:

RgbColorVector(unsigned int r,unsigned int g,unsigned int b) 
     : pM(3,1,CV_8UC1) 
    { 

Dieses Mitglied Initialisiererliste genannt wird

+1

Wenn das temporäre Objekt mit cv :: Mat erstellt wird, weist es Speicher im Heap zu, um Farbe zu speichern Werte.Alle Klassenmitglieder von temporärem cv :: Mat werden zusammen mit dem Datenzeiger in pM-Klassenmitglieder kopiert, so dass diese Pixeldaten auf dem Haufen gültig bleiben, ist das richtig? –

+0

'cv :: Mat' kann den Heap intern verwenden oder auch nicht, das ist bis zu dieser Klasse und sollte für den Benutzer keinen Unterschied machen. C++ verwendet eine Wertesemantik, d. H. Klassen sollten sich wie "int" verhalten. Wenn Sie einen Wert kopieren, spielt es keine Rolle, was nach dem Original passiert, die Kopie ist davon nicht betroffen. – sp2danny

+0

@BattleBeast Das ist richtig. Es funktioniert ähnlich wie ein geteilter Zeiger. –

0

Sie rufen die cv::Mat Kopie ctor, so dass die neu erstellte temporäre cv::Mat wird bereits konstruierte pM verschoben. Daher bleibt pM konstruiert und gültig bis zum Ende des Bereichs des übergeordneten Objekts. pM wird niemals zerstört, noch werden Daten von pM gespeichert.

OpenCV fügt jedoch eine weitere Dimension der Indirektion hinzu. cv::Mat speichert Daten über einen Zeiger, um eine Duplizierung zu verhindern, wenn sie nicht benötigt wird, eine Form der flachen Kopie. Es verwaltet selbst geschickt Daten auf dem Heap und entfernt die Daten, wenn es nicht mehr von anderen cv::Mat angesprochen wird. Also, auch ein cv::Mat Objekt auf dem Stapel enthält einige Daten auf dem Heap und andere Details auf dem Stapel, was der Fall mit Ihrem pM ist. Nicht relevant, aber da Sie über den Umfang von cv::Mat und seine Daten verwirrt sind, fügte ich einige Details hinzu.

Edit: temporäre Variable, verschieben und Standard Ctor dank sp2danny

+2

Es wird nicht der Kopierkonstruktor aufgerufen, zuerst wird der Standardkonstruktor implizit aufgerufen, dann wird eine explizite Konstruktion eines temporären get aufgerufen, dann move-assignment – sp2danny

+0

... was kann korrigiert werden, indem eine Initialisierungsliste im Konstruktor für pM verwendet wird http://en.cppreference.com/w/cpp/language/initializer_list – xaxxon

Verwandte Themen