Imagine this code:
class Base {
public:
virtual void foo(){}
};
class Derived: public Base {
public:
int i;
void foo() override {}
void do_derived() {
std::cout << i;
}
};
int main(){
Base *ptr = new Base;
Derived * static_ptr = static_cast<Derived*>(ptr);
static_ptr->i = 10; // Why does this work?
static_ptr->foo(); // Why does this work?
return 0;
}
Why do I get the result 10 on the console? I wonder because I thought the ptr is a pointer to a base object. Therefore the object doesn't contain a int i or the method do_derived(). Is a new derived-Object automatically generated?
When I declare a virtual do_derived() method in the Base class too, then this one is chosen, but why?
int* i = new int{1};
delete i;
std::cout << *i << std::endl;
This will also "work", if the definition of working is that the code will compile and execute.
However, it is clearly undefined behavior and there are no guarantees as to what might happen.
In your case, the code compiles as static_cast won't perform any checks, it just converts the pointer. It is still undefined behavior to access memory that hasn't been allocated and initialized though.
As mentioned in the comments, "happens to do what you expected" is not the same as "works".
Let's make a few modifications:
#include <iostream>
#include <string>
class Base{
public:
virtual void foo(){
std::cout << "Base::foo" << std::endl;
}
};
class Derived: public Base{
public:
int a_chunk_of_other_stuff[1000000] = { 0 };
std::string s = "a very long string so that we can be sure we have defeated SSO and allocated some memory";
void foo() override {
std::cout << "Derived::foo" << std::endl;
}
void do_derived() {
std::cout << s << std::endl;
}
};
int main(){
Base *ptr = new Base;
Derived * static_ptr = static_cast<Derived*>(ptr);
static_ptr -> foo(); // does it though?
static_ptr -> do_derived(); // doesn't work?
static_ptr->a_chunk_of_other_stuff[500000] = 10; // BOOM!
return 0;
}
Sample Output:
Base::foo
Process finished with exit code 11
In this case, none of the operations did what we expected. The assignment into the array caused a segfault.
The statement:
Base *ptr = new Base;
Doesn't always allocate sizeof(Base) - it would probably allocate more memory. Even if it does allocate exact sizeof(Base) bytes, it doesn't necessarily mean any byte access after this range (i.e. sizeof(Base)+n, n>1) would be invalid.
Hence let's assume the size of class Base is 4 bytes (due to virtual function table in most compiler's implementation, on a 32-bit platform). However, the new operator, the heap-management API, the memory management of OS, and/or the hardware does allocate 16 bytes for this allocation (assumption). This makes additional 12 bytes valid! It makes the following statement valid:
static_ptr->i = 10;
Since now it tries to write 4 bytes (sizeof(int), normally) after the first 4 bytes (size of polymorphic class Base).
The function call:
static_ptr->foo();
would simply make a call to Derived::foo since the pointer is of type Derived, and nothing is wrong in it. The compiler must call Derived::foo. The method Derived::foo doesn't even try to access any data member of derived class (and even base class).
Had you called:
static_ptr->do_derived();
which is accessing i member of derived. It would still be valid, since:
The function call is always valid, till method tries to access data-member (i.e. accesses something out of this pointer).
Data-member access became valid due to memory allocation (UD
behaviour)
Note that following is perfectly valid:
class Abc
{
public:
void foo() { cout << "Safe"; }
};
int main()
{
Abc* p = NULL;
p->foo(); // Safe
}
The call it valid, since it translates to:
foo(NULL);
where foo is:
void foo(Abc* p)
{
// doesn't read anything out of pointer!
}
why does this static cast work?
Because static cast is compile time checker. There is a relationship between Base and Derived. Since it has relationship, static cast believe's that relationship and believe's the programmer too. So As a programmer, you should make sure that Base object should not be static casted to derived class object.
Related
I tried looking for an answer, but couldn't find anything helpful to understand what happens behind the scenes.
Assuming that we have the following:
class Base
{
public:
virtual int request() = 0;
void* operator new(long unsigned int size) = delete;
};
class Derived final : public Base
{
public:
int request() override;
};
int Derived::request()
{
return 2;
}
On the base class I have deleted the new operator because I don't want to be able to create pointers.
I know it might not make any sense, it is just an experiment.
When I call g++ gives me a compilation error to tell me that the new operator is deleted. Exactly what I expected.
int main()
{
auto d = std::make_unique<Derived>();
int value = d->request();
std::cout << "Request value is " << value << "." << std::endl;
return 0;
}
But if I create a shared_ptr the code compiles and runs fine.
auto d = std::make_shared<Derived>();
Why is this code valid when creating shared pointers. The operator is not being used in this case?
std::make_shared use global placement new version of the operator new, by std::allocator::construct
It is necessary to store object and atomic reference counter in the same block of memory, so that shared_ptr template works almost equal to intrusive smart pointer.
You can not prevent std::make_shared to construct a smart pointer.
#include <iostream>
#include <memory>
class Base
{
public:
virtual void foo() = 0;
};
class Derived : public Base
{
public:
void foo() override { std::cout << "Derived" << std::endl; }
};
class Concrete
{
public:
void Bar() { std::cout << "concrete" << std::endl; }
};
int main()
{
std::unique_ptr<Concrete> ConcretePtr = nullptr;
ConcretePtr->Bar();
std::unique_ptr<Base> BasePtr;
BasePtr->foo();
return 0;
}
I assume declaring a unique_ptr to a concrete type Concrete, allocates memory for an object of type Concrete and the unique_ptr starts pointing to it. Is my assumption/understanding correct ? I ask because ConcretePtr->Bar(); prints "concrete" to the console. But, if I make a unique pointer to an interface Base, it does not know the exact type of object that I need and does not allocate/acquire resources in the memory.
This fails at BasePtr->foo(); with BasePtr._Mypair._Myval2 was nullptr.
Why does the first declaration std::unique_ptr<Concrete> ConcretePtr = nullptr; allocate an object by itself ? what if I did not want it to be pointing to some real object at that very line of code, but wanted only a smart pointer ?
Now if I change the declaration to be std::unique_ptr<Concrete> ConcretePtr; and the Concrete type to be the following,
class Concrete
{
int ConcreteNum;
public:
void Bar()
{
std::cout << "concrete" << std::endl;
ConcreteNum = 38;
std::cout << ConcreteNum << std::endl;
}
};
it fails at ConcreteNum = 38; complaining that this was nullptr; if this this was nullptr then why and how did the earlier call (where Concrete did not have any state ConcreteNum) to Bar work ?
Moreover why does it not fail at ConcretePtr->Bar(); (this -> requires a concrete object, does it not ? what was this here ?) but inside Bar, in that assignment ?
I see the same issue with std::shared_ptr as well. I'm not very sure of the difference between declaration, initialization & assignment. Please help me understand.
I'm using MSVC.
The unique_ptr models a pointer. That is, it's an object that points to another object.
Initialising the unique_ptr with nullptr creates it in the state where it is not pointing to or owning another object.
It's like saying Concrete* p = nullptr.
Initialise it in one of these ways:
std::unique_ptr<Concrete> p{new Concrete()};
or
std::unique_ptr<Concrete> p; // = nullptr is implied.
p.reset(new Concrete());
or, better:
std::unique_ptr<Concrete> p = std::make_unique<Concrete>();
or simply:
auto p = std::make_unique<Concrete>();
But be careful in this case if you really want to be pointing to the Base interface:
std::unique_ptr<Base> p = std::make_unique<Derived>();
or
std::unique_ptr<Base> p = nullptr;
p = std::make_unique<Derived>(); // assignment from rvalue ref of compatible unique_ptr.
std::unique_ptr<Concrete> ConcretePtr = nullptr;
I assume declaring a unique_ptr to a concrete type Concrete, allocates memory for an object of type Concrete and the unique_ptr starts pointing to it. Is my assumption/understanding correct ?
Well, you can trivially check. Write a default constructor for Concrete that prints something out so you can tell when an instance is created. Run the smallest possible program (just the line above in main). Did you see the expected output?
You should be checking this stuff before asking a question (and probably after reading the documentation), but to save you time: no, that line doesn't construct an object of type Concrete.
You can also check explicitly whether unique_ptr is managing an object, with
if (!ConcretePtr) {
std::cout << "ConcretePtr doesn't point to anything\n";
} else {
std::cout << "ConcretePtr owns an object\n";
}
This check is also trivial, and you could easily do it before asking a question.
I ask because ConcretePtr->Bar(); prints "concrete" to the console
This is a bad test because if the pointer is a nullptr, it's undefined behaviour. If you care whether the pointer is a nullptr, you should check that explicitly before dereferencing it, as above.
To demonstrate why this test is confusing you (and you should use the ones above in preference), consider a likely implementation of non-virtual member functions (recall they get an implicit this pointer):
// void Concrete::Bar() implemented as
void Concrete_Bar(Concrete *this)
// and ConcretePtr->Bar() implemented as
Concrete_Bar(ConcretePtr.get());
so, you just passed a nullptr to a function that ignores its only parameter, and you never tested the thing you thought you did.
#include"iostream"
using namespace std;
class base{
public:
void f()
{
cout<<"base f:"<<endl; // prints base f:
}
};
int main()
{
base *b; // even same out put with " base *b =NULL; "
b->f();
return 0;
}
O/p : base f:
can any one please explain how the function is getting called without assigning the object to the pointer .
Thanks.
Call of member-function with not initialized (or initialized to 0) pointer to object is undefined behaviour, however it may works since there is no attempts to access variables of object and there is no vtable here. You can look at this function like
void f_base(base* p)
{
cout << "base f:" << endl;
}
there is no access - there is no error, on all modern-compilers it will work, but it can be changed anytime.
This is invalid code, but since nothing in base::f() accesses a member variable, no invalid memory is getting touched.
If you add a member and try to print it out in the function, you will almost undoubtedly get a crash.
You need to use new
i.e.
base *b; // even same out put with " base *b =NULL; "
should be
base *b = new base;
... Need a delete to prevent memory leaks
I'm wondering why I get an exception on the delete part in one case here, but not in the other.
No exception case
#include <iostream>
using namespace std;
class A
{
public:
~A() { cout << "A dtor" << endl; }
};
class B : public A
{
public:
int x;
~B() { cout << "B dtor" << endl; }
};
A* f() { return new B; }
int _tmain(int argc, _TCHAR* argv[])
{
cout << sizeof(B) << " " << sizeof(A) << endl;
A* bptr= f();
delete bptr;
}
Herethe output is 4 1 .. A dtor, since A has 1 byte for identity and B has 4 because of int x.
Exception case
#include <iostream>
using namespace std;
class A
{
public:
~A() { cout << "A dtor" << endl; }
};
class B : public A
{
public:
virtual ~B() { cout << "B dtor" << endl; }
};
A* f() { return new B; }
int _tmain(int argc, _TCHAR* argv[])
{
cout << sizeof(B) << " " << sizeof(A) << endl;
A* bptr= f();
delete bptr;
}
Here the output is 4 1 .. A dtor, since A has 1 byte for identity and B has 4 because of the vptr that's needed for its virtual destructor.
But then a debug assertion fails inside the delete call (_BLOCK_TYPE_IS_VALID).
Environment
I'm running Windows 7 with Visual Studio 2010 SP1Rel.
See this post
A quick summary:
You are telling the machine to delete an instance of A
As this is a class which we call through pointer/reference maybe we should use a virtual table (VT)?
There is no virtual members in A thus no VT is used
We call the standard destructor of A…
Bang! We are trying to delete class A but it happens that the pointer
has lead us to object of B which contains VT which A didn't know of.
sizeof(A) is 1 (as AFAIK it’s not legal to have size equal 0) and
sizeof(B) is 4 (due to presence of VT). We wish to delete 1 byte, but
there is a block of 4 bytes. Due to DEBUG heap monitoring, the error
was caught.
The solution of course is to declare the base class's (A's) dtor as virtual so B's dtor will always be called.
EDIT: For the first case, here's what the standard has to say:
§5.3 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 the second alternative (delete
array) if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined.
So both cases lead us to the realm of undefined behavior which of course differs from one implementation to the other. But it stands to reason that for most implementations the first case is easier to handle or at least easier to contemplate than the second which is just an esoteric anti-pattern.
As others have pointed out, you are deleting an object whose static type is different from its dynamic type, and since the static type doesn't have a virtual destructor, you get undefined behavior. This includes the behavior of sometimes working and sometimes not working as you are seeing. However, I think you are interested in a little deeper understanding of what is happening with your particular compiler.
Class A has no members at all, so its data layout ends up looking like this:
struct A {
};
Since class B derives from class A, class A becomes embedded within B. When class B has no virtual functions, the layout ends up looking like this:
struct B {
A __a_part;
int x;
};
The compiler can convert a B* to an A* by just taking the address of __a_part, as if the compiler had a function like this:
A* convertToAPointer(B* bp) { return &bp->__a_part; }
Since __a_part is the first member of B, the B* and the A* point to the same address.
Code like this:
A* bptr = new B;
delete bptr;
Is effectively doing something like this:
// Allocate a new B
void* vp1 = allocateMemory(sizeof(B));
B* bp = static_cast<B*>(vp1);
bp->B(); // assume for a second that this was a legal way to construct
// Convert the B* to an A*
A* bptr = &bp->__a_part;
// Deallocate the A*
void* vp2 = ap;
deallocateMemory(vp2);
In this case, vp2 and vp1 are the same. The system is allocating and deallocating the same memory address, so the program runs without an error.
When class B has a virtual member function (the destructor in this case). The compiler adds a virtual table pointer, so class B ends up looking like this:
struct B {
B_vtable* __vptr;
A __a_part;
};
The issue here is that __a_part is no longer the first member, and the convertToAPointer operation will now change the address of the pointer, so vp2 and vp1 no longer point to the same address. Since a different memory location is being deallocated than the one that was allocated, you get the error.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
C++: why is new needed?
Why cant I use malloc to allocate space for my objects when they are children of a class containing virtual functions? This is really frustrating. Is there a good reason?
The following program illustrates the problem. It segfaults on line 27, where I call aa->f()
#include <iostream>
#include <cstdlib>
class A
{
public:
virtual int f() {return 1;}
};
class B
{
public:
int f() {return 1;}
};
class Aa : public A {};
class Bb : public B {};
int main()
{
Aa* aa = (Aa*)malloc(sizeof(Aa));
Aa* aan = (Aa*)new Aa();
Bb* bb = (Bb*)malloc(sizeof(Bb));
std::cout << bb->f() << std::endl;
std::cout << aan->f() << std::endl;
std::cout << aa->f() << std::endl;
return 0;
}
Version info: g++ (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5
A common way to implement virtual functions is to have a pointer to a "virtual table" or vtable at a negative offset from the object. This table is needed to figure out what virtual function to call. This is why just malloc'ing space doesn't work.
malloc only allocates memory, but does not create an object. So, the line
Aa* aa = (Aa*)malloc(sizeof(Aa));
allocates a region of memory that is large enough to hold an A, but contains garbage. As others pointed out, this also means that the pointer to the vtable will not be set (I got that one from #David Rodríguez's comment on another answer), which is required to dispatch calls to virtual functions. Since B does not contain virtual functions, no such problem arises. It would happen with B too, however, if B contained any data-members that require initialization, such as this:
class B
{
public:
B() : foo(new int()) {}
int f() {return *foo;}
private:
int * foo;
};
The line
Aa* aan = (Aa*)new Aa();
can do without the cast:
Aa* aan = new Aa();
The reason is that malloc knows nothing about C++ constuctors and consequently does not call them. You can call the constuctors yourself using placement new:
int main()
{
Aa* aa = (Aa*)malloc(sizeof(Aa));
new(aa)Aa;
Aa* aan = (Aa*)new Aa();
Bb* bb = (Bb*)malloc(sizeof(Bb));
new(bb)Bb;
std::cout << bb->f() << std::endl;
std::cout << aan->f() << std::endl;
std::cout << aa->f() << std::endl;
aa->~Aa();
free(aa);
delete aan;
bb->~Bb();
free(bb);
return 0;
}
Note that you have to manually call the destructors before freeing such memory.
Don't use malloc, use new - malloc does not call constructors.
When you do A * a = new A(); the compiler will allocate memory, set up the vtable pointer for A and call the constructor. When you call a virtual function, the vtable is used to actually find the function.
When you do A * a = (A *) malloc(...); the compiler will allocate memory, which will contain random data. When you call a virtual function, it'll look at the (garbage) vtable and call some random location.
A class with virtual functions look something like this internally:
struct Foo {
void * vtable;
int aClassMemberVar;
};
Calling a virtual function looks at the "hidden" vtable pointer, which points to the class vtable, a linked list of pointers to functions. So this vtable pointer must be initialized, and malloc doesn't do that.
Surely because the Virtual Function Table isn't getting created properly?
malloc does not call the constructor of the class, so your object is not initailized properly, hence it seg faults. Use new to allocate memory when using C++. BTW, there is no need to cast the pointer returned from new.
The good reason is called virtual tables. Objects of types that have virtual methods have a table of pointers pointing to the address of the actual virtual methods to be called. These are called virtual tables or v-tables.