This is related to a question posted yesterday.
class A
{
public:
mutable int x;
A()
{
static int i = 0;
x = i;
i++;
std::cout << " A()" << std::endl;
}
~A()
{
std::cout << "~A()" << std::endl;
}
void foo() const
{
x = 1;
};
};
class B
{
public:
const A & a;
B(const A & a) : a(a)
{
std::cout << " B()" << std::endl;
}
~B()
{
std::cout << "~B()" << std::endl;
}
void doSomething()
{
a.foo();
};
};
int main()
{
B b((A()));
b.doSomething();
}
Now, a's destructor is called before the call to doSomething. However, the call works although the function basically changes a member of A. Is it not the same instance. No other A's are created. I used the static inside A's constructor to keep track of that. Can anyone explain?
This is undefined behavior, so there is no language standard explanation.
However, the destructor of A doesn't do anything to the memory area where x is stored, so if you look there later the value might just still be there. Or if you try to write to the address, the address is still there. You are just not allowed to do that.
Bo is correct.
In addition, you could check the address where 'A' is stored, and that should confirm that that address simply hasn't been reused yet (keep in mind a destructor frees ("releases") the memory, but doesn't traverse the data structure setting all of the bits back to 0; that would be inefficient).
If, for example, you find that A is stored on top of the stack, then you are simply fortunate that your subsequent function call doesn't pass in a parameter, as that would overwrite A's memory region.
Your reference is invalid after ~A() and it is undefined behavior
~A() calls destructors of all members of A in addition
Try so for example
class B
{
public:
const std::string & a;
B(const std::string & a) : a(a)
{
std::cout << " B()" << std::endl;
}
~B()
{
std::cout << "~B()" << std::endl;
}
void doSomething()
{
std::cout << "a = " << a << std::endl;
};
};
int main()
{
B b(std::string("I love C++ so much!"));
b.doSomething();
}
Expanding on Bo's answer.
For a temporary to exist, space will be reserved on the stack. This space is actually reserved as long as the semantics require the temporary to exist, and may then be reuse for something else.
If you were trying to use the memory after it has been reused, you would observe a strange behavior (the very definition of undefined behavior being that anything can happen). As it is, you luck out and the memory is still there, in the state you expect it to.
Example:
#include <iostream>
struct A {
A(): p(0) {}
~A() { if (p) { std::cout << *p << "\n"; } }
int* p;
};
int bar0();
void bar1(int i);
int main() {
A a;
{
int x = 4; a.p = &x;
}
{
int y = bar0(); bar1(y);
}
}
int bar0() { return 7; }
void bar1(int i) { std::cout << i << "\n"; }
Here, the compiler may choose to reuse the space of x for y, or just do anything it wants, and thus you're actually printing garbage.
Here is gcc 4.3.4 (and 4.5.1) output (courtesy of ideone):
7
4
Meaning that the space is not reused with those...
Related
In the following code an object is overwritten with a new object of same type, where a lambda-expression creates a closure that uses this of the old object. The old address (this) remains the same, the new object has the same layout, so this should be ok and not UB. But what about non trivial objects or other cases?
struct A {
void g(A& o, int v) {
o = A{.x = v, .f = [this]{
std::cout << "f" << this->x << '\n';
}};
}
int x{0};
std::function<void()> f;
~A() {
std::cout << "dtor" << x << '\n';
}
};
void test() {
A a;
a.g(a, 2);
a.f();
}
You are not actually replacing any object. You are just assigning from another object to the current one. o = simply calls the implicit copy assignment operator which will copy-assign the individual members from the temporary A constructed in the assignment expression with A{...}.
The lambda is going to capture this from this in g, not from the temporary object.
std::function will always keep a copy of the lambda referring to the original object on which g was called and since that is its parent object, it cannot outlive it.
So there is no problem here. The only exception would be that you call f during the destruction of the A object, in which case using the captured pointer may be forbidden.
Here is a slightly modified code with a corner case. I create a temporary in a function and call g on it passing it a more permanent object. The temporary vanishes and the long life object now has a closure refering to an object after its end of life. Invoking f is UB:
#include <iostream>
#include <functional>
struct A {
void g(A& o, int v) {
o = A{ .x = v, .f = [this] {
std::cout << "f" << this->x << ' ' << this << '\n';
} };
}
int x{ 0 };
std::function<void()> f;
~A() {
std::cout << "dtor" << x << ' ' << this << '\n';
}
};
void test(A& a) {
A b{ 2 };
b.g(a, 3);
}
int main() {
A a{ 1 };
std::cout << a.x << '\n';
test(a);
std::cout << a.x << '\n';
a.f(); // UB because a.f uses an object after its end of life
}
The output is:
1
dtor3 0135F9C0
dtor2 0135FA30
3
f341072 0135FA30
dtor3 0135FAA8
proving that the invocation of a.f() tried to use the object at address 0135FA30 (in that specific run) after it has been destroyed.
I have written a Base class and a Derived class with the respective data members as you could see in the code below. Now in main function I have created new Base class objects pointed by Derived class pointers using static_cast for it.
#include <iostream>
#include <vector>
class Base {
public:
int b;
Base() : b(2){};
int get_b() const;
};
class Derived : public Base {
public:
int d;
Derived() : d(4){};
int get_d() const;
};
int Base::get_b() const { return b; }
int Derived::get_d() const { return d; }
int main() {
std::vector<Derived *> bArray;
bArray.push_back(static_cast<const Derived *>(new Base()));
bArray.push_back(static_cast<const Derived *>(new Base()));
std::vector<Derived *>::iterator bArrayIt = bArray.begin();
for (; bArrayIt != bArray.end(); ++bArrayIt) {
std::cout << (*bArrayIt)->get_b() << std::endl;
std::cout << (*bArrayIt)->get_d() << std::endl;
}
}
Output:
2
0
Now in the code I have tried accessing the data members of the derived class using the Derived pointers but I expected it to return a compilation error or 'ArrayOutOfBoundIndex' or segmentation_fault because the object is of Base type as space is allocated for base object only but instead got value of that member 'd' as zero. According to what I know about static_cast it just alters the pointer type and not allocate memory,but here not only we could access the memory not allocated but the value has been set to 0 initially beforehand, so I did a small experiment of my own.
#include <iostream>
#include <string.h>
class State;
class Base;
class Derived;
class State {
public:
static bool flag;
};
bool State::flag = true;
class Base : public State {
public:
int a;
int b;
int c;
Base() : a(2), b(4), c(16){};
int get_a() { return a; }
int get_b() { return b; }
int get_c() { return c; }
};
class Derived : public Base {
public:
int d;
int e;
int f;
Derived() : d(6), e(8), f(12){};
int set_d(int ds) { d = ds; }
int get_d() { return d; }
int get_e() { return e; }
int get_f() { return f; }
};
int main() {
Derived *d[2];
d[0] = static_cast<Derived *>(new Base());
d[1] = static_cast<Derived *>(new Base());
std::cout << d[0]->get_a() << std::endl;
std::cout << d[0]->get_d() << std::endl;
d[0]->set_d(100);
std::cout << d[0]->get_d() << std::endl;
int *i = reinterpret_cast<int *>(d[0]);
std::cout << (*i) << std::endl;
i++;
std::cout << (*i) << std::endl;
i++;
std::cout << (*i) << std::endl;
i++;
std::cout << (*i) << std::endl;
i++;
std::cout << (*i) << std::endl;
i++;
std::cout << (*i) << std::endl;
std::cout << "Let's move onto d[1]" << std::endl;
int *j = reinterpret_cast<int *>(d[1]);
std::cout << (*j) << std::endl;
j++;
std::cout << (*j) << std::endl;
j++;
std::cout << (*j) << std::endl;
j++;
std::cout << (*j) << std::endl;
j++;
std::cout << (*j) << std::endl;
j++;
std::cout << (*j) << std::endl;
return 0;
}
Output:
2
0
100
2
4
16
100
0
0
Let's move into d[1]
2
4
16
0
0
0
The output was according to what I got before.
My Questions:
Why is the variable allowed to access memory which is not allocated by the new keyword or the compiler and how is it able to do so?
If it is able to access memory location why doesn't the compiler give any runtime error or compile time error and is there any method to make the compiler do so?
Now if the memory is allocated by the compiler or somehow, what implications does it have on the memory i.e. if the Base class is of 12 bytes and the Derived class is of 24 bytes then will every object creation of Base Type and using static_cast for it to be pointed by Derived pointer will allocate 24 bytes of memory?
Why is the variable allowed to access memory which is not allocated by the new keyword or the compiler and how is it able to do so?
Because, there is nothing which will prevent you from doing it. C++ gives a lot of freedom for programmers, but with this freedom comes responsibility (e.g. making sure that you don't access unallocated memory).
If it is able to access memory location why doesn't the compiler give any runtime error or compile time error?
It's Undefined Behaviour, which means, that at runtime your program becomes unpredictable. If you're lucky, it may crash for some reason (e.g. segfault), but it doesn't have to. At compile time, this operation is prefectly legal in terms of language, so there is nothing compiler will complain about. You'll probably have to use some static analysis tool to catch such mistakes.
Now if the memory is allocated by the compiler or somehow, what implications does it have on the memory i.e. if the Base class is of 12 bytes and the Derived class is of 24 bytes then will every object creation of Base Type and using static_cast for it to be pointed by Derived pointer will allocate 24 bytes of memory?
static_cast has nothing to do with memory allocation. It just converts type of pointer. Allocating memory for object is up to operator new and size of memory allocated won't be affected by any pointer conversion.
This question already has an answer here:
Placement new and assignment of class with const member
(1 answer)
Closed 4 years ago.
The 2 print statements print different numbers. As far as I can see I'm not doing any dodgy const_cast here so I'm not sure what UB I could have possibly committed.
Is this code well-formed?
Can the compiler rely on the fact that A::num is const so it's allowed to print the same number ?
Code:
struct A
{
const int num = 100;
A() {}
A(int in) : num{in} {}
void call()
{
new (this) A{69};
}
};
int main()
{
A a;
std::cout << a.num << '\n';
a.call();
std::cout << a.num << '\n';
}
No, your code has UB. Remove the const on num and you don't get any UB anymore.
The problem is that the standard provides a guarantee that a const object doesn't change. But if you reuse the same storage, then you can "modify" the const object in a way.
[basic.life]p8 explicitly prohibits this by saying that the old name of the object only refers to the new object under certain conditions. One of them is that your class doesn't have any const members. So by extension, your second a.num is UB, as the a refers to the old destructed object.
However, there are two ways to avoid this UB. First, you can store the pointer to the new object:
struct A *new_ptr;
struct A {
// [...]
void call() {
new_ptr = new (this) A{69};
}
};
int main()
{
A a;
std::cout << a.num << '\n';
a.call();
std::cout << new_ptr->num << '\n'; // ok
}
Or use std::launder:
std::cout << std::launder(&a)->num << '\n'; // second access
I wanted to restrict a specific class to be creatable on the stack only (not via allocation). The reason for this is that on the stack, the object which lifetime has begun last, will be the first to be destroyed, and I can create a hierarchy. I did it like this:
#include <cstddef>
#include <iostream>
class Foo {
public:
static Foo createOnStack() {
return {};
}
~Foo () {
std::cout << "Destructed " << --i << std::endl;
}
protected:
static int i;
Foo () {
std::cout << "Created " << i++ << std::endl;
}
Foo (const Foo &) = delete;
};
int Foo::i = 0;
The constructor normally should push the hierarchy stack, and the destructor pops it. I replaced it here for proof of concept. Now, the only way you can use such an object is by storing it in a temporary reference like this:
int main() {
Foo && a = Foo::createOnStack();
const Foo& b = Foo::createOnStack();
return 0;
}
My question now is, how safe is this with the C++ standard? Is there still a way to legally create a Foo on the heap or hand it down from your function into another frame (aka return it from your function) without running into undefined behaviour?
EDIT: link to example https://ideone.com/M0I1NI
Leaving aside the protected backdoor, C++17 copy elision breaks this in two ways:
#include<iostream>
#include<memory>
struct S {
static S make() {return {};}
S(const S&)=delete;
~S() {std::cout << '-' << this << std::endl;}
private:
S() {std::cout << '+' << this << std::endl;}
};
S reorder() {
S &&local=S::make();
return S::make();
}
int main() {
auto p=new S(S::make()),q=new S(S::make()); // #1
delete p; delete q;
reorder(); // #2
}
The use of new is obvious and has been discussed.
C++17 also allows prvalues to propagate through stack frames, which means that a local can get created before a return value and get destroyed while that return value is alive.
Note that the second case already existed (formally in C++14 and informally long before) in the case where local is of type S but the return value is some other (movable) type. You can't assume in general that even automatic object lifetimes nest properly.
I am trying to learn C++ and from what I've read in books and on SO:
If I use auto x = new Object(); x is a pointer to address of Object and this is in dynamic memory and exists until I delete it.
However if I use Object x; or auto x = Object() it only lasts until it goes out of scope.
In an example they have shown this:
void foo()
{
Point p = Point(0,0);
} // p is now destroyed.
What I don't understand is what happens when I return a object when I don't use new? Will it be a copy of the object?
Here is an example of what I am not sure about:
class Object
{
public:
int X;
static Object Foo(int y)
{
Object result;
result.X = y;
return result;
}
};
class TestContainer
{
public:
void Run()
{
for(auto i = 0; i < 10; i++)
{
_objects.at(i) = Object::Foo(i + (rand() % 10 + 1));
}
}
private:
std::vector<Object> _objects;
};
void main()
{
TestContainer tc;
while(true)
{
tc.Run();
}
}
Note I haven't tested this code but I think it illiterates my confusion. In my main function I instantiate TestContainer and endless call it's Run method. This in turn loops calling a static Foo method on Object that returns a copy of a new Object, which is stored in a vector.
My question is, what happens with all the Object's? If I replace element 2 in the objects vector with a new Object, is the old value now "out of scope" and is deleted?
Will it be a copy of the object?
Yes.
Or a move could be used instead, or the entire thing could be optimised away to produce only one actual object in your final, compiled program.
But, basically, yes.
If I replace element 2 in the objects vector with a new Object, is the old value now "out of scope" and is deleted?
Yes.
As an aside, you're using at on elements that don't exist; to add elements, use insert or push_back.
A simple class like this behaves much like a POD variable. o1=o2 copies the fields, element-wise. So the target Object of an assignment does not get deleted but overwritten.
Objects which go out of scope "go away" (because the stack is unwound) like e.g. an int.
Here is a run-able example that I believe illustrates this behavior:
#include <iostream>
using namespace std;
class Foo {
private:
int id;
public:
Foo(int x)
: id(x)
{
cout << this->id << " is created" << endl;
}
Foo(const Foo& rhs)
: id(rhs.id)
{
cout << "copied " << this->id << endl;
}
Foo& operator=(Foo rhs){
this->id=rhs.id;
cout << "assigned " << this->id << endl;
return *this;
}
~Foo(){
cout << this->id << " is destroyed" << endl;
}
int getID(){
return this->id;
}
};
Foo bar(){
Foo f1 = Foo(1);
cout << f1.getID() << " from bar" << endl;
return f1;
}
int main(){
Foo f2 = bar();
cout << f2.getID() << " from main" << endl;
return 0;
}
This produces this output:
1 is created
1 from bar
1 from main
1 is destroyed
From this, I'm not seeing a copy or an assignment. I suspect what is happening is that both f1 and f2 are referencing the same instance of the object in memory. The object is not being de-allocated when the f1 reference goes out of scope because the object has another reference assigned to it.