Why is this not a memory leak in C++?












3















A couple of months ago I asked this question where I asked why there was a memory leak. Apparently, I forgot a virtual destructor.



Now I'm struggling to understand why this is not a memory leak:



#include <iostream>
#include <vector>
#include <memory>


using namespace std;

class Base{
public:
explicit Base(double a){
a_ = a;
}
virtual void fun(){
cout << "Base " << a_ << endl;
}

protected:
double a_;
};


class Derived : public Base{
public:
Derived(double a, double b): Base(a), b_{b}{
}
void fun() override{
cout << "Derived " << a_ << endl;
}
private:
double b_;
};



int main() {

vector<unique_ptr<Base> > m;

for(int i=0; i<10; ++i){
if(i%2 == 0){
m.emplace_back(make_unique<Base>(i));
}else{
m.emplace_back(make_unique<Derived>(i, 2*i));
}
}

for(const auto &any:m){
any->fun();
}

return 0;
}


Note that I do not have a virtual destructor for Base.



I thought that because I have a vector m of type unique_ptr<Base> only the destructor from Base gets called and my variable b_ in Derived would leak but according to valgrind this is not the case.
Why is this not a memory leak?



I have tested this with valgrind-3.13.0










share|improve this question

























  • May be of interest to know that std::shared_ptr will capture the type, and call the correct destructor. There is additional overhead using a std::shared_ptr rather than a std::unique_ptr. However, using the correct smart pointer really should be made so as to capture intent. Using the "wrong" one so as to get the type captured, all to avoid making the destructor virtual is going down a bad path (imo).

    – Eljay
    Nov 19 '18 at 13:12
















3















A couple of months ago I asked this question where I asked why there was a memory leak. Apparently, I forgot a virtual destructor.



Now I'm struggling to understand why this is not a memory leak:



#include <iostream>
#include <vector>
#include <memory>


using namespace std;

class Base{
public:
explicit Base(double a){
a_ = a;
}
virtual void fun(){
cout << "Base " << a_ << endl;
}

protected:
double a_;
};


class Derived : public Base{
public:
Derived(double a, double b): Base(a), b_{b}{
}
void fun() override{
cout << "Derived " << a_ << endl;
}
private:
double b_;
};



int main() {

vector<unique_ptr<Base> > m;

for(int i=0; i<10; ++i){
if(i%2 == 0){
m.emplace_back(make_unique<Base>(i));
}else{
m.emplace_back(make_unique<Derived>(i, 2*i));
}
}

for(const auto &any:m){
any->fun();
}

return 0;
}


Note that I do not have a virtual destructor for Base.



I thought that because I have a vector m of type unique_ptr<Base> only the destructor from Base gets called and my variable b_ in Derived would leak but according to valgrind this is not the case.
Why is this not a memory leak?



I have tested this with valgrind-3.13.0










share|improve this question

























  • May be of interest to know that std::shared_ptr will capture the type, and call the correct destructor. There is additional overhead using a std::shared_ptr rather than a std::unique_ptr. However, using the correct smart pointer really should be made so as to capture intent. Using the "wrong" one so as to get the type captured, all to avoid making the destructor virtual is going down a bad path (imo).

    – Eljay
    Nov 19 '18 at 13:12














3












3








3








A couple of months ago I asked this question where I asked why there was a memory leak. Apparently, I forgot a virtual destructor.



Now I'm struggling to understand why this is not a memory leak:



#include <iostream>
#include <vector>
#include <memory>


using namespace std;

class Base{
public:
explicit Base(double a){
a_ = a;
}
virtual void fun(){
cout << "Base " << a_ << endl;
}

protected:
double a_;
};


class Derived : public Base{
public:
Derived(double a, double b): Base(a), b_{b}{
}
void fun() override{
cout << "Derived " << a_ << endl;
}
private:
double b_;
};



int main() {

vector<unique_ptr<Base> > m;

for(int i=0; i<10; ++i){
if(i%2 == 0){
m.emplace_back(make_unique<Base>(i));
}else{
m.emplace_back(make_unique<Derived>(i, 2*i));
}
}

for(const auto &any:m){
any->fun();
}

return 0;
}


Note that I do not have a virtual destructor for Base.



I thought that because I have a vector m of type unique_ptr<Base> only the destructor from Base gets called and my variable b_ in Derived would leak but according to valgrind this is not the case.
Why is this not a memory leak?



I have tested this with valgrind-3.13.0










share|improve this question
















A couple of months ago I asked this question where I asked why there was a memory leak. Apparently, I forgot a virtual destructor.



Now I'm struggling to understand why this is not a memory leak:



#include <iostream>
#include <vector>
#include <memory>


using namespace std;

class Base{
public:
explicit Base(double a){
a_ = a;
}
virtual void fun(){
cout << "Base " << a_ << endl;
}

protected:
double a_;
};


class Derived : public Base{
public:
Derived(double a, double b): Base(a), b_{b}{
}
void fun() override{
cout << "Derived " << a_ << endl;
}
private:
double b_;
};



int main() {

vector<unique_ptr<Base> > m;

for(int i=0; i<10; ++i){
if(i%2 == 0){
m.emplace_back(make_unique<Base>(i));
}else{
m.emplace_back(make_unique<Derived>(i, 2*i));
}
}

for(const auto &any:m){
any->fun();
}

return 0;
}


Note that I do not have a virtual destructor for Base.



I thought that because I have a vector m of type unique_ptr<Base> only the destructor from Base gets called and my variable b_ in Derived would leak but according to valgrind this is not the case.
Why is this not a memory leak?



I have tested this with valgrind-3.13.0







c++ memory-leaks valgrind undefined-behavior unique-ptr






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 19 '18 at 16:01







user7431005

















asked Nov 19 '18 at 10:36









user7431005user7431005

980316




980316













  • May be of interest to know that std::shared_ptr will capture the type, and call the correct destructor. There is additional overhead using a std::shared_ptr rather than a std::unique_ptr. However, using the correct smart pointer really should be made so as to capture intent. Using the "wrong" one so as to get the type captured, all to avoid making the destructor virtual is going down a bad path (imo).

    – Eljay
    Nov 19 '18 at 13:12



















  • May be of interest to know that std::shared_ptr will capture the type, and call the correct destructor. There is additional overhead using a std::shared_ptr rather than a std::unique_ptr. However, using the correct smart pointer really should be made so as to capture intent. Using the "wrong" one so as to get the type captured, all to avoid making the destructor virtual is going down a bad path (imo).

    – Eljay
    Nov 19 '18 at 13:12

















May be of interest to know that std::shared_ptr will capture the type, and call the correct destructor. There is additional overhead using a std::shared_ptr rather than a std::unique_ptr. However, using the correct smart pointer really should be made so as to capture intent. Using the "wrong" one so as to get the type captured, all to avoid making the destructor virtual is going down a bad path (imo).

– Eljay
Nov 19 '18 at 13:12





May be of interest to know that std::shared_ptr will capture the type, and call the correct destructor. There is additional overhead using a std::shared_ptr rather than a std::unique_ptr. However, using the correct smart pointer really should be made so as to capture intent. Using the "wrong" one so as to get the type captured, all to avoid making the destructor virtual is going down a bad path (imo).

– Eljay
Nov 19 '18 at 13:12












3 Answers
3






active

oldest

votes


















5














Deleting an object via a polymorphic pointer when the base class doesn't have a virtual destructor is undefined behaviour.



Undefined behaviour may mean your code leaks memory, crashes or works perfectly.



In this case the runtime library presumably allocated a single block of memory for your object and is able to delete that block correctly even when it is pointed to by a pointer of a different type. This is probably true for most runtimes but there are no guarantees. E.g. when using malloc() and free() you don't need to supply the size of the malloc() to free(), the same is happening here.



If you had defined a destructor in Derived you would see that it is not being called.






share|improve this answer































    3














    There would be a memory leak if b was an object that had resources (memory, network...) because of undefined behavior.



    Here, by chance, the derived destructor doesn't do anything and the memory for the object is freed properly (this time). But anything more than built-in/trivially destructible types could trigger a memory leak (I suggest you try a vector of size 10 for instance).



    BTW, o is not used?






    share|improve this answer


























    • I don't think that UB can cause a memory leak reliably. And there are lots of types which are trivially destructible aside from built-in types.

      – felix
      Nov 19 '18 at 11:30











    • @Fair enough. That's why I mentioned resources in my first phrase, but wasn't clear enough afterwards.

      – Matthieu Brucher
      Nov 19 '18 at 11:34



















    1














    It doesn't leak memory because of how your C++ implementation behaves, but it is undefined behavior and you should fix it.



    It's not a memory leak in this case because...





    1. std::make_unique allocates using new:




      template<class T, class... Args> unique_ptr<T> make_unique(Args&&... args); [...]
      Returns: unique_­ptr<T>(new T(std::forward<Args>(args)...)).
      [unique.ptr.create]




    2. std::unique_ptr deallocates using the std::default_delete which uses operator delete.



    It doesn't matter from the perspective of memory leaks that the types are different, because delete will still be called with the pointer to the object allocated by new.



    It would also be a memory leak if Derived did not have a trivial destructor. If it held a std::vector for example, that vector's destructor would be called by ~Derived. If it's not called, the storage for the vector would (detectably) leak.



    See also: How does delete work?



    However, all of this is undefined behavior per the C++ standard, so it might theoretically stop working any time. See [expr.delete/3].




    In a single-object delete expression, if the static type of the object to be deleted is different from its dynamic type and the selected deallocation function (see below) is not a destroying operator delete, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined.







    share|improve this answer























      Your Answer






      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "1"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53372775%2fwhy-is-this-not-a-memory-leak-in-c%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      3 Answers
      3






      active

      oldest

      votes








      3 Answers
      3






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      5














      Deleting an object via a polymorphic pointer when the base class doesn't have a virtual destructor is undefined behaviour.



      Undefined behaviour may mean your code leaks memory, crashes or works perfectly.



      In this case the runtime library presumably allocated a single block of memory for your object and is able to delete that block correctly even when it is pointed to by a pointer of a different type. This is probably true for most runtimes but there are no guarantees. E.g. when using malloc() and free() you don't need to supply the size of the malloc() to free(), the same is happening here.



      If you had defined a destructor in Derived you would see that it is not being called.






      share|improve this answer




























        5














        Deleting an object via a polymorphic pointer when the base class doesn't have a virtual destructor is undefined behaviour.



        Undefined behaviour may mean your code leaks memory, crashes or works perfectly.



        In this case the runtime library presumably allocated a single block of memory for your object and is able to delete that block correctly even when it is pointed to by a pointer of a different type. This is probably true for most runtimes but there are no guarantees. E.g. when using malloc() and free() you don't need to supply the size of the malloc() to free(), the same is happening here.



        If you had defined a destructor in Derived you would see that it is not being called.






        share|improve this answer


























          5












          5








          5







          Deleting an object via a polymorphic pointer when the base class doesn't have a virtual destructor is undefined behaviour.



          Undefined behaviour may mean your code leaks memory, crashes or works perfectly.



          In this case the runtime library presumably allocated a single block of memory for your object and is able to delete that block correctly even when it is pointed to by a pointer of a different type. This is probably true for most runtimes but there are no guarantees. E.g. when using malloc() and free() you don't need to supply the size of the malloc() to free(), the same is happening here.



          If you had defined a destructor in Derived you would see that it is not being called.






          share|improve this answer













          Deleting an object via a polymorphic pointer when the base class doesn't have a virtual destructor is undefined behaviour.



          Undefined behaviour may mean your code leaks memory, crashes or works perfectly.



          In this case the runtime library presumably allocated a single block of memory for your object and is able to delete that block correctly even when it is pointed to by a pointer of a different type. This is probably true for most runtimes but there are no guarantees. E.g. when using malloc() and free() you don't need to supply the size of the malloc() to free(), the same is happening here.



          If you had defined a destructor in Derived you would see that it is not being called.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Nov 19 '18 at 10:58









          Alan BirtlesAlan Birtles

          8,595933




          8,595933

























              3














              There would be a memory leak if b was an object that had resources (memory, network...) because of undefined behavior.



              Here, by chance, the derived destructor doesn't do anything and the memory for the object is freed properly (this time). But anything more than built-in/trivially destructible types could trigger a memory leak (I suggest you try a vector of size 10 for instance).



              BTW, o is not used?






              share|improve this answer


























              • I don't think that UB can cause a memory leak reliably. And there are lots of types which are trivially destructible aside from built-in types.

                – felix
                Nov 19 '18 at 11:30











              • @Fair enough. That's why I mentioned resources in my first phrase, but wasn't clear enough afterwards.

                – Matthieu Brucher
                Nov 19 '18 at 11:34
















              3














              There would be a memory leak if b was an object that had resources (memory, network...) because of undefined behavior.



              Here, by chance, the derived destructor doesn't do anything and the memory for the object is freed properly (this time). But anything more than built-in/trivially destructible types could trigger a memory leak (I suggest you try a vector of size 10 for instance).



              BTW, o is not used?






              share|improve this answer


























              • I don't think that UB can cause a memory leak reliably. And there are lots of types which are trivially destructible aside from built-in types.

                – felix
                Nov 19 '18 at 11:30











              • @Fair enough. That's why I mentioned resources in my first phrase, but wasn't clear enough afterwards.

                – Matthieu Brucher
                Nov 19 '18 at 11:34














              3












              3








              3







              There would be a memory leak if b was an object that had resources (memory, network...) because of undefined behavior.



              Here, by chance, the derived destructor doesn't do anything and the memory for the object is freed properly (this time). But anything more than built-in/trivially destructible types could trigger a memory leak (I suggest you try a vector of size 10 for instance).



              BTW, o is not used?






              share|improve this answer















              There would be a memory leak if b was an object that had resources (memory, network...) because of undefined behavior.



              Here, by chance, the derived destructor doesn't do anything and the memory for the object is freed properly (this time). But anything more than built-in/trivially destructible types could trigger a memory leak (I suggest you try a vector of size 10 for instance).



              BTW, o is not used?







              share|improve this answer














              share|improve this answer



              share|improve this answer








              edited Nov 19 '18 at 11:32

























              answered Nov 19 '18 at 10:40









              Matthieu BrucherMatthieu Brucher

              13.7k32140




              13.7k32140













              • I don't think that UB can cause a memory leak reliably. And there are lots of types which are trivially destructible aside from built-in types.

                – felix
                Nov 19 '18 at 11:30











              • @Fair enough. That's why I mentioned resources in my first phrase, but wasn't clear enough afterwards.

                – Matthieu Brucher
                Nov 19 '18 at 11:34



















              • I don't think that UB can cause a memory leak reliably. And there are lots of types which are trivially destructible aside from built-in types.

                – felix
                Nov 19 '18 at 11:30











              • @Fair enough. That's why I mentioned resources in my first phrase, but wasn't clear enough afterwards.

                – Matthieu Brucher
                Nov 19 '18 at 11:34

















              I don't think that UB can cause a memory leak reliably. And there are lots of types which are trivially destructible aside from built-in types.

              – felix
              Nov 19 '18 at 11:30





              I don't think that UB can cause a memory leak reliably. And there are lots of types which are trivially destructible aside from built-in types.

              – felix
              Nov 19 '18 at 11:30













              @Fair enough. That's why I mentioned resources in my first phrase, but wasn't clear enough afterwards.

              – Matthieu Brucher
              Nov 19 '18 at 11:34





              @Fair enough. That's why I mentioned resources in my first phrase, but wasn't clear enough afterwards.

              – Matthieu Brucher
              Nov 19 '18 at 11:34











              1














              It doesn't leak memory because of how your C++ implementation behaves, but it is undefined behavior and you should fix it.



              It's not a memory leak in this case because...





              1. std::make_unique allocates using new:




                template<class T, class... Args> unique_ptr<T> make_unique(Args&&... args); [...]
                Returns: unique_­ptr<T>(new T(std::forward<Args>(args)...)).
                [unique.ptr.create]




              2. std::unique_ptr deallocates using the std::default_delete which uses operator delete.



              It doesn't matter from the perspective of memory leaks that the types are different, because delete will still be called with the pointer to the object allocated by new.



              It would also be a memory leak if Derived did not have a trivial destructor. If it held a std::vector for example, that vector's destructor would be called by ~Derived. If it's not called, the storage for the vector would (detectably) leak.



              See also: How does delete work?



              However, all of this is undefined behavior per the C++ standard, so it might theoretically stop working any time. See [expr.delete/3].




              In a single-object delete expression, if the static type of the object to be deleted is different from its dynamic type and the selected deallocation function (see below) is not a destroying operator delete, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined.







              share|improve this answer




























                1














                It doesn't leak memory because of how your C++ implementation behaves, but it is undefined behavior and you should fix it.



                It's not a memory leak in this case because...





                1. std::make_unique allocates using new:




                  template<class T, class... Args> unique_ptr<T> make_unique(Args&&... args); [...]
                  Returns: unique_­ptr<T>(new T(std::forward<Args>(args)...)).
                  [unique.ptr.create]




                2. std::unique_ptr deallocates using the std::default_delete which uses operator delete.



                It doesn't matter from the perspective of memory leaks that the types are different, because delete will still be called with the pointer to the object allocated by new.



                It would also be a memory leak if Derived did not have a trivial destructor. If it held a std::vector for example, that vector's destructor would be called by ~Derived. If it's not called, the storage for the vector would (detectably) leak.



                See also: How does delete work?



                However, all of this is undefined behavior per the C++ standard, so it might theoretically stop working any time. See [expr.delete/3].




                In a single-object delete expression, if the static type of the object to be deleted is different from its dynamic type and the selected deallocation function (see below) is not a destroying operator delete, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined.







                share|improve this answer


























                  1












                  1








                  1







                  It doesn't leak memory because of how your C++ implementation behaves, but it is undefined behavior and you should fix it.



                  It's not a memory leak in this case because...





                  1. std::make_unique allocates using new:




                    template<class T, class... Args> unique_ptr<T> make_unique(Args&&... args); [...]
                    Returns: unique_­ptr<T>(new T(std::forward<Args>(args)...)).
                    [unique.ptr.create]




                  2. std::unique_ptr deallocates using the std::default_delete which uses operator delete.



                  It doesn't matter from the perspective of memory leaks that the types are different, because delete will still be called with the pointer to the object allocated by new.



                  It would also be a memory leak if Derived did not have a trivial destructor. If it held a std::vector for example, that vector's destructor would be called by ~Derived. If it's not called, the storage for the vector would (detectably) leak.



                  See also: How does delete work?



                  However, all of this is undefined behavior per the C++ standard, so it might theoretically stop working any time. See [expr.delete/3].




                  In a single-object delete expression, if the static type of the object to be deleted is different from its dynamic type and the selected deallocation function (see below) is not a destroying operator delete, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined.







                  share|improve this answer













                  It doesn't leak memory because of how your C++ implementation behaves, but it is undefined behavior and you should fix it.



                  It's not a memory leak in this case because...





                  1. std::make_unique allocates using new:




                    template<class T, class... Args> unique_ptr<T> make_unique(Args&&... args); [...]
                    Returns: unique_­ptr<T>(new T(std::forward<Args>(args)...)).
                    [unique.ptr.create]




                  2. std::unique_ptr deallocates using the std::default_delete which uses operator delete.



                  It doesn't matter from the perspective of memory leaks that the types are different, because delete will still be called with the pointer to the object allocated by new.



                  It would also be a memory leak if Derived did not have a trivial destructor. If it held a std::vector for example, that vector's destructor would be called by ~Derived. If it's not called, the storage for the vector would (detectably) leak.



                  See also: How does delete work?



                  However, all of this is undefined behavior per the C++ standard, so it might theoretically stop working any time. See [expr.delete/3].




                  In a single-object delete expression, if the static type of the object to be deleted is different from its dynamic type and the selected deallocation function (see below) is not a destroying operator delete, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined.








                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Nov 19 '18 at 12:16









                  palotasbpalotasb

                  2,37111420




                  2,37111420






























                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53372775%2fwhy-is-this-not-a-memory-leak-in-c%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      How to change which sound is reproduced for terminal bell?

                      Title Spacing in Bjornstrup Chapter, Removing Chapter Number From Contents

                      Can I use Tabulator js library in my java Spring + Thymeleaf project?