Why is this not a memory leak in C++?
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
add a comment |
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
May be of interest to know thatstd::shared_ptr
will capture the type, and call the correct destructor. There is additional overhead using astd::shared_ptr
rather than astd::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
add a comment |
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
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
c++ memory-leaks valgrind undefined-behavior unique-ptr
edited Nov 19 '18 at 16:01
user7431005
asked Nov 19 '18 at 10:36
user7431005user7431005
980316
980316
May be of interest to know thatstd::shared_ptr
will capture the type, and call the correct destructor. There is additional overhead using astd::shared_ptr
rather than astd::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
add a comment |
May be of interest to know thatstd::shared_ptr
will capture the type, and call the correct destructor. There is additional overhead using astd::shared_ptr
rather than astd::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
add a comment |
3 Answers
3
active
oldest
votes
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.
add a comment |
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?
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
add a comment |
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...
std::make_unique
allocates usingnew
:
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]
std::unique_ptr
deallocates using thestd::default_delete
which usesoperator 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.
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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.
add a comment |
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.
add a comment |
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.
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.
answered Nov 19 '18 at 10:58
Alan BirtlesAlan Birtles
8,595933
8,595933
add a comment |
add a comment |
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?
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
add a comment |
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?
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
add a comment |
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?
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?
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
add a comment |
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
add a comment |
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...
std::make_unique
allocates usingnew
:
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]
std::unique_ptr
deallocates using thestd::default_delete
which usesoperator 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.
add a comment |
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...
std::make_unique
allocates usingnew
:
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]
std::unique_ptr
deallocates using thestd::default_delete
which usesoperator 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.
add a comment |
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...
std::make_unique
allocates usingnew
:
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]
std::unique_ptr
deallocates using thestd::default_delete
which usesoperator 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.
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...
std::make_unique
allocates usingnew
:
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]
std::unique_ptr
deallocates using thestd::default_delete
which usesoperator 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.
answered Nov 19 '18 at 12:16
palotasbpalotasb
2,37111420
2,37111420
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
May be of interest to know that
std::shared_ptr
will capture the type, and call the correct destructor. There is additional overhead using astd::shared_ptr
rather than astd::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