class base {
int a;
protected:
template<class T>
class derived;
public:
base() {}
virtual ~base() {}
virtual void func() {}
static base* maker();
};
template <class T>
class base::derived
: public base
{
public:
derived() {}
virtual ~derived() {}
virtual void func() {
this->~derived(); //<--is this legal?
new (this) derived<int>(); //<--is this legal?
}
};
base* base::maker() {
return new derived<double>();
}
int main() {
base* p = base::maker(); //p is derivedA<double>
p->func(); //p is now derivedA<int>
delete p; //is the compiler allowed to call ~derived<double>()?
}
This is a Short, Self Contained, Correct (Compilable), Example of my code (which is basically reinventing any_iterator for my own growth).
The question reduces down to: is it undefined behavior to destroy this and reconstruct this with a different type virtually derived from the same base, when neither has any additional members over the shared base? Specifically, are compilers allowed to call keep track of the static type, or is that technically nonconforming?
[EDIT] Several people pointed out that compilers may call the incorrect destructor if derivedA is created on the stack. (1) I can't find anything in the standard that allows compilers to do so. (2) That was aside from what I intended by my question, so I have changed the code to show that derived cannot be placed on the stack. base can still be on the stack though.
I think that's clearly not OK.
As a preface, and object's lifetime can indeed be ended by calling the destructor, but you're only allowed (and required) to construct a new object of the same type in its place:
{
Foo x;
x.~Foo();
::new (&x) Foo;
} // x.~Foo() must be a valid call here!
Remember that at scope's end the destructor will be invoked!
But in your case you're constructing an entirely different object:
::new (&x) Bar; // Bar = DerivedA<int>
Clearly if sizeof(Bar) exceeds sizeof(Foo) this cannot be OK.
(Perhaps if you can make additional guarantees on the object sizes, as well as on the alignment guarantees, we can think about this further.)
Update: Even if you're thinking, OK, so those types are derived from the same base, so invoking the destructor brings virtual happiness, I'm still pretty sure that that's a violation. In this static setting, the compiler may well resolve the virtual call statically, so you're breaking the compiler's assumptions if you change the dynamic type of the thing pointed to by &x.
Update 2: Another thought on the same matter: The static type of *&x is known, and I think you have to respect that. In other words, the compiler has no reason to factor in the possibility that the static type of a local variable changes.
I'm pretty sure that is not valid code for several reasons:
If the inserted type isn't the same size bad things will happen (I'm not quite sure but I think the standard doesn't make to much promises about the size of a type, so from the theoretical vantage point it might be hard to prove that they have the same size (through in praxis they most likely will))
if the type of the variable is statically known (probably due to it beeing constructed on the stack, but it in theory it could do the same thing if it can see the allocaiton and prove that the pointer couldn't have been modified) the compiler can feel free to statically reolve virtual method calls (e.g. a destructor) and use them which would obviously break the code
Even if the type of the variable is not statically known I'm quite sure the compiler can assume that it's type won't change during it's lifetime (The pointer can't change inside the function so it should be able to assume the pointed to type doesn't either). Therefore while it can't statically resolve methods it could possibly reuse the vmt pointer from previous invocations of virtual methods (e.g. the one changing the type)
Edit: now that I think about it doesn't this break strict aliasing rules, since after the placement new this points to a non compatible type? granted it isn't explicietly accessed in the function again, but I don't think it can be guranteed that there won't be accesses inserted by the compiler (highly unlikely though). Anyways this would mean that the compiler can assume that this kind of action won't happen.
Edit: When looking at the new C++ Standard I found that [basic.life] (§3.8.5) gives what is basically the same thing as an example of undefined behaviour (it doesn't actually destroy the object, but I don't see how that could make matters better):
#include<cstdlib>
structB{
virtual void f();
void mutate();
virtual ~B();
};
struct D1:B { void f(); };
struct D2:B { void f(); };
void B::mutate(){
new(this)D2; //reuses storage—ends the lifetime of *this
f(); //undefined behavior
...=this; //OK, this points to valid memory
}
void g(){
void* p = std::malloc(sizeof(D1) + sizeof(D2));
B* pb = new(p)D1;
pb->mutate();
&pb; //OK: pb points to valid memory
void* q = pb; //OK: pb points to valid memory
pb->f(); //undefined behavior, lifetime of *pb hasended
}
This should prove that this is not allowed.
Related
Look at this little snippet:
struct A {
virtual ~A() { }
};
struct B { };
bool fn() {
A *volatile a = new A;
return dynamic_cast<B *>(a);
}
Is the compiler allowed to remove the dynamic_cast altogether, and transform dynamic_cast to a simple nullptr;?
The reason of this question is this answer.
Notes:
Assume that volatile means that the compiler cannot assume anything about a, because it's volatile. Here's a question why.
The fact that dynamic_cast may not be allowed to be removed is that there could be a type somewhere in the program, which derives from both A and B.
Yes. dynamic_cast has no observable behavior beyond its return value.
The compiler is aware of the static type pointed to by a.
So under the as-if rule, the compiler is free to evaluate the dynamic cast at compile time.
In fact:
struct A { virtual ~A() {} };
struct B:A {};
bool foo() {
A* a = new A;
return dynamic_cast<B*>(a);
}
the above dynamic cast statement can also be optimized to return false; The new cannot be omitted without whole program optimization as someone could overload the global operator new; once it is proven that no global operator new is overloaded, it could even optimize out the call to new A, as neither allocating memory by the default operator new, nor creating an A nor destroying one has any observable side effects.
Yes, a compiler can omit the call to dynamic_cast as per as-if rule 1) if and only if it can prove that the only valid result of the call is false. That's simple.
The tricky part is to prove that the only valid result of dynamic_cast is false. You can prove that iff there is no class in your whole program that inherits both from A and B.
Now I am not very versed in this part, but I think you can do that when you create the binary and have all the types in your program only if it's an executable (not a library) and only if the program doesn't dynamically link to other libraries.
1) the dynamic_cast on pointers doesn't have side effect, it doesn't throw
Actually I am thinking about trivially destructible objects, not only about POD (I am not sure POD can have base class).
When I read this explanation for is_trivially_destructible from cppreference I notice this:
Storage occupied by trivially destructible objects may be reused without calling the destructor.
So, it is safe to do that:
struct A {
int a;
};
struct B : A {
int b;
};
int main() {
A* a = new B;
delete a;
}
B::~B() won't be called - and AFAIK (please correct if I am wrong) the entire memory will be freed. And B::~B() for sure is trivial.
I know this code smells badly, but my question is only about safeness of this code...
No, this is not allowed. [expr.delete]/p3, emphasis mine:
In the first alternative (delete object), if the static type of the
object to be deleted is different from its dynamic type, 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.
In fact, the committee fairly recently rejected a proposal to make deleting a POD via a pointer-to-base well-defined.
I'm fairly new to Pass By Reference, and I HAVE to make sure I understand this correctly. I have to convert all my Heap memory to Stack memory because my professor said so, and I'm stuck on two concepts.
What is the best way to store a reference in a class? I originally had member objects as non pointers, but noticed the deconstructor would be called on the member variable when the object (not member object) was popped off the stack. This makes me think it was a copy, and not actually a reference.
Here is an example of what I had originally:
class B
{
public:
B();
~B();
};
class A
{
private:
B b;
public:
A();
~A();
setB(B& bVar);
};
void A::setB(B& bVar)
{
b = bVar;
}
My solution was to change it to a pointer so it didn't call the deconstructor, but I'M NOT SURE IF THIS IS THE CORRECT WAY TO DO IT. Here was my solution:
class B
{
public:
B();
~B();
};
class A
{
private:
B* b;
public:
A();
~A();
setB(B& bVar);
};
void A::setB(B& bVar)
{
b = &bVar;
}
My second question is kind of related. I'm not sure what exactly happens when you have:
object1 = object2&.
Is object1 a copy or is it actually another identifier for object2?
References behave like symbolic aliases to instances, and are in some respects like "pointers" that can't (shouldn't) be null. For the sake of this explanation, I'll refer to them below as though they were pointers.
When you have a T&, it means that it is pointing to a T, and is not itself a copy.
When you have a T = T&, it means you'll get a copy (or a copy of a copy) depending on how the constructor or assignment operator are defined.
When you have an R& = L, it means you'll get copy of L into whatever the R& is pointing to (provided the assignment operator of R permits this).
Concerning the "correct" way of storing references, I would ask at least these questions:
Is it acceptable for the member reference to remain the same throughout the containing object's lifetime?
Will instances of the containing type always be destroyed before the object(s) pointed to by the member reference?
If both are true, then simply declaring and appropriately initializing a member T& should suffice:
class B
{
// details...
};
class A
{
B &_b;
public:
A(B &b) :
_b(b)
{}
};
Otherwise, despite the requirements imposed upon you, the situation might call for something like shared_ptr<> or similar.
References to objects living on the stack, in-turn held by other objects that themselves may be constructed in such a way that they will outlive their reference's lifespan are merely pointers waiting to dangle.
Consider copying, or arguing that heap-allocated memory is the better option.
If you are uncertain of the reference network induced by your program, you need to redesign it.
EDIT:
It is important to note that, when passing a reference to a function (const T& in particular), there are certain situations in which it can be elided by the compiler. For example: When such a function is inlined, references can be replaced by more efficient addressing logic than if they were required to be pointers.
In this respect, they are not pointers.
I found the following snippet in the C++03 Standard under 5.3.5 [expr.delete] p3:
In the first alternative (delete object), if the static type of the object to be deleted is different from its dynamic type, the static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual destructor or the behavior is undefined. In the second alternative (delete array) if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined.
Quick review on static and dynamic types:
struct B{ virtual ~B(){} };
struct D : B{};
B* p = new D();
Static type of p is B*, while the dynamic type of *p is D, 1.3.7 [defns.dynamic.type]:
[Example: if a pointer p whose static type is “pointer to class B” is pointing to an object of class D, derived from B, the dynamic type of the expression *p is “D.”]
Now, looking at the quote at the top again, this would mean that the follwing code invokes undefined behaviour if I got that right, regardless of the presence of a virtual destructor:
struct B{ virtual ~B(){} };
struct D : B{};
B* p = new D[20];
delete [] p; // undefined behaviour here
Did I misunderstand the wording in the standard somehow? Did I overlook something? Why does the standard specify this as undefined behaviour?
Base* p = new Base[n] creates an n-sized array of Base elements, of which p then points to the first element. Base* p = new Derived[n] however, creates an n-sized array of Derived elements. p then points to the Base subobject of the first element. p does not however refer to the first element of the array, which is what a valid delete[] p expression requires.
Of course it would be possible to mandate (and then implement) that delete [] p Does The Right Thing™ in this case. But what would it take? An implementation would have to take care to somehow retrieve the element type of the array, and then morally dynamic_cast p to this type. Then it's a matter of doing a plain delete[] like we already do.
The problem with that is that this would be needed every time an array of polymorphic element type, regardless of whether the polymorphism is used on not. In my opinion, this doesn't fit with the C++ philosophy of not paying for what you don't use. But worse: a polymorphic-enabled delete[] p is simply useless because p is almost useless in your question. p is a pointer to a subobject of an element and no more; it's otherwise completely unrelated to the array. You certainly can't do p[i] (for i > 0) with it. So it's not unreasonable that delete[] p doesn't work.
To sum up:
arrays already have plenty of legitimate uses. By not allowing arrays to behave polymorphically (either as a whole or only for delete[]) this means that arrays with a polymorphic element type are not penalized for those legitimate uses, which is in line with the philosophy of C++.
if on the other hand an array with polymorphic behaviour is needed, it's possible to implement one in terms of what we have already.
It's wrong to treat an array-of-derived as an array-of-base, not only when deleting items. For example even just accessing the elements will usually cause disaster:
B *b = new D[10];
b[5].foo();
b[5] will use the size of B to calculate which memory location to access, and if B and D have different sizes, this will not lead to the intended results.
Just like a std::vector<D> can't be converted to a std::vector<B>, a pointer to D[] shouldn't be convertible to a B*, but for historic reasons it compiles anyway. If a std::vector would be used instead, it would produce a compile time error.
This is also explained in the C++ FAQ Lite answer on this topic.
So delete causes undefined behavior in this case because it's already wrong to treat an array in this way, even though the type system can't catch the error.
Just to add to the excellent answer of sth - I have written a short example to illustrate this issue with different offsets.
Note that if you comment out the m_c member of the Derived class, the delete operation will work well.
Cheers,
Guy.
#include <iostream>
using namespace std;
class Base
{
public:
Base(int a, int b)
: m_a(a)
, m_b(b)
{
cout << "Base::Base - setting m_a:" << m_a << " m_b:" << m_b << endl;
}
virtual ~Base()
{
cout << "Base::~Base" << endl;
}
protected:
int m_a;
int m_b;
};
class Derived : public Base
{
public:
Derived()
: Base(1, 2) , m_c(3)
{
}
virtual ~Derived()
{
cout << "Derived::Derived" << endl;
}
private:
int m_c;
};
int main(int argc, char** argv)
{
// create an array of Derived object and point them with a Base pointer
Base* pArr = new Derived [3];
// now go ahead and delete the array using the "usual" delete notation for an array
delete [] pArr;
return 0;
}
IMHO this has to do with limitation of arrays to deal with constructor/destructor. Note that, when new[] is called, compiler forces to instantiate only default constructor. In the same way when delete[] is called, compiler might look for only the destructor of calling pointer's static type.
Now in the case of virtual destructor, Derived class destructor should be called first followed by the Base class. Since for arrays compiler might see the static type of calling object (here Base) type, it might end up calling just Base destructor; which is UB.
Having said that, it's not necessarily UB for all compilers; say for example gcc calls destructor in proper order.
I think it all comes down to the zero-overhead principle. i.e. the language doesn't allow storing information about the dynamic type of elements of the array.
I have the following contrived example (coming from real code):
template <class T>
class Base {
public:
Base(int a):x(a) {}
Base(Base<T> * &other) { }
virtual ~Base() {}
private:
int x;
};
template <class T>
class Derived:public Base<T>{
public:
Derived(int x):Base<T>(x) {}
Derived(Derived<T>* &other): Base<T>(other) {}
};
int main() {
Derived<int> *x=new Derived<int>(1);
Derived<int> y(x);
}
When I try to compile this, I get:
1X.cc: In constructor ‘Derived<T>::Derived(Derived<T>*&) [with T = int]’:
1X.cc:27: instantiated from here
1X.cc:20: error: invalid conversion from ‘Derived<int>*’ to ‘int’
1X.cc:20: error: initializing argument 1 of ‘Base<T>::Base(int) [with T = int]’
1) Clearly gcc is being confused by the constructors. If I remove the reference
from the constructors, then the code compiles. So my assumption is that something goes wrong
with up-casting pointer references. Can someone tell me what is going on here?
2) A slightly unrelated question. If I were to do something horrendous like "delete other" in the constructor (bear with me),
what happens when someone passes me a pointer to something on the stack ?
E.g. Derived<int> x(2);
Derived<int> y(x);
where
Derived(Derived<T>*& other) { delete other;}
How can I make sure that pointer is legitimately pointing to something on the heap?
Base<T> is a base type of Derived<T>, but Base<T>* is not a base type of Derived<T>*. You can pass a derived pointer in place of a base pointer, but you can't pass a derived pointer reference in place of a base pointer reference.
The reason is that, suppose you could, and suppose the constructor of Base were to write some value into the reference:
Base(Base<T> * &other) {
Base<T> *thing = new Base<T>(12);
other = thing;
}
You've just written a pointer to something which is not a Derived<T>, into a pointer to Derived<T>. The compiler can't let this happen.
You cannot convert a reference to a pointer to Derived to a reference to a pointer to Base. (Templates don't contribute to the issue here, so removed from my example below.)
If you want to defer responsibility for a pointer, use a smart pointer type. Smart pointer types can represent the "responsibility to delete" that raw pointers cannot. Examples include std::auto_ptr and boost::shared_ptr, among many others.
Why you cannot upcast pointer references:
struct Base {};
struct Derived : Base {};
struct Subclass : Base {};
int main() {
Derived d;
Derived* p = &d;
Derived*& d_ptr = p;
Base*& b_ptr = d_ptr; // this is not allowed, but let's say it is
Base b;
b_ptr = &b; // oops! d_ptr no longer points to a Derived!
Subclass s;
b_ptr = &s; // oops! d_ptr no longer points to a Derived!
}
When you pass your 'other' parameter to the Base ctor, you're trying to do the same thing as b_ptr = d_ptr above.
You make sure that pointer points to something on the heap by writing that in your documentation and relying on the caller to abide by that. If whoever calls your constructor passes a stack pointer, all bets are off, and it's not your fault - you can try to catch the problem early, but no guarantees.
That's how the standard library works - often it'll catch obvious errors, but it's not required to, and it's up to the caller to make sure they're not doing anything stupid.
Your x variable is not a pointer, it should be if you want to assign a new Derived<int> to it.
As for deleting things on the stack, don't do it. There is no way to tell whether you have been passed the address of something on the stack or on the heap (indeed, the C++ standard doesn't even acknowledge the existence of a stack). The lesson here is that you shouldn't be deleting things that you don't own, especially if you have no way of telling where they came from.
Not sure why do you want reference to the pointer. Why not
Base(Base<T> * other) { }
and
Derived(Derived<T>* other): Base<T>(other) {}
That should work.
And, like other answered, I don't think you can legitimately know whether the pointer is pointing into heap.
Edit: why can't one do what you're trying to: consider example:
Derived1<int> *x = new Derived1<int>
Base<int> **xx =&x;
Derived2<int> y;
*xx = &y;
Where Derived1 and Derived2 are different classes derived from Base? Would you think it's legitimate? Now that x of type Derived1* points to Derived2?