I'm trying to understand POD types and how they are allocated and initialized on the stack.
Given
class A {
public:
A();
int x;
};
class B {
public:
int x;
};
int func()
{
A a;
B b;
}
Am I correct in saying that b is allocated after a but initialized prior to a? By that I mean
that the space is allocate for a and b in the order that they are declared but b is initialized
when the space is allocated and a is initialized when it is declared?
I read a very good FAQ about PODs and Aggregated here
What are Aggregates and PODs and how/why are they special?
One of the things he said is:
The lifetime of objects of non-POD class type begins when the constructor has finished and ends when the destructor has finished. For POD classes, the lifetime begins when storage for the object is occupied and finishes when that storage is released or reused.
So I'm trying understand the details of how PODs are allocated and initialized and how that is
different from non-PODs.
No. a is allocated and initialized first, and b is allocated and initialized second. C++ programs are executed statement by statement. Since the memory is automatic, there is no explicit allocation happening anyway -- it's all taken care of automatically.
(For instance, in typical call-stack implementations used on desktop operating systems, the memory is and has always been there and doesn't need to be allocated at all, just addressed.)
You have zero guarantees of any kind for the order in memory that A and B are allocated.
If A and B both had constructors, a's would be called before b's. But POD types, which you're asking about (and which B is) are not initialized at all with this syntax, so the question is moot.
The question of object initialization in relation to when the storage is allocated doesn't make much sense anyway. For example, most compilers here will allocate space for A and B in a single stack pointer move. Given that there is no way a conforming C++ program can detect such a thing (what does it even mean?), the compiler can do pretty much whatever it wants, though.
These are local variables, they are not "allocated" in the common sense, you can just consider them being there. (How is left to implementation; common way is to use a processor-supported stack. In that case all the storage for all local objects is taken on the stack at function entry).
Initialization always happens in the order of declarations. Here it means A::A() is called for a, then B::B() is called for b.
Related
I'm a c++ beginner and now reading the C++ Primer. I have some problem about the destrucor:
in chapter 13.1.3: "In a destructor, there is nothing akin to the constructor initializer list to control how members are destroyed; the destruction part is implicit. What happens when a member is destroyed depends on the type of the member. Members of class type are destroyed by running the member’s own destructor. The built-in types do not have destructors, so nothing is done to destroy members of built-in type."
So if I have such a class:
class Foo {
~Foo() { delete pi; }
int *pi;
int i;
};
When an instance of Foo is going to be destroyed, according to the above text, the built in type variable int i will not be destroyed? Then how it will exist since the object to which it belongs has been destroyed.
In chapter 13.1.4: "The HasPtr class that we have used in the exercises is a good example (§ 13.1.1,p.499). That class allocates dynamic memory in its constructor. The synthesized destructor will not delete a data member that is a pointer. Therefore, this class needs to define a destructor to free the memory allocated by its constructor."
I understand that the synthesized destructor does not de-allocate the memory that has been allocated by new and is pointed to by plain pointers, but here in the text, it indicates that the pointer object itself is also not deleted by the synthesized pointer. Still the example of class Foo, does that mean that after an object of Foo class is destroyed, the pointer pi will not be destroyed ( so becomes an invalid pointer )?
What the spec means is that no code is run to clean up int i. It simply ceases to exist. Its memory is part of Foo and whenever a Foo instance is released, then i goes with it.
For pointers the same is true, the pointer itself will simply disappear (it's really just another number), but the pointer might point at something that needs to also be released. The compiler doesn't know if that is the case, so you have to write a destructor.
This is why things like std::shared_ptr exist; they are clever pointers (aka 'smart') and the compiler does know if it points at something that needs to be released and will generate the correct code to do so. This is why you should always use smart pointers instead of 'naked' ones (like int *p).
A destructor always destroys all members, just like a constructor always creates all members. You can't stop it from doing that.
according to the above text, the built in type variable int i will not be destroyed?
It will be destroyed. What the book says is that destroying an int doesn't perform any actions (it's said to be a "no-op"), in the sense that it translates to 0 machine instructions.
Same for pointers (not the data they point to), so the wording in (2) is sloppy, and your initial understanding was correct.
according to the above text, the built in type variable int i will not be destroyed?
It will be destroyed, but nothing is done to destroy it. The destruction is trivial.
int *pi member variable also has a fundamental type, and same applies to it.
Then how it will exist
It won't exist.
does that mean that after an object of Foo class is destroyed, the pointer pi will not be destroyed ( so becomes an invalid pointer )?
It does not mean that. All sub objects are always destroyed both by all synthesised destructors, as well as all user defined destructors.
Deleting a pointer, and destroying a pointer are two separate things. Deletion destroys the pointed object and deallocates the dynamic memory. Destruction ends the lifetime of the pointer itself, and is trivial because pointers are fundamental types.
Destruction is what the synthesised - as well as all other - constructors do to the member. A synthesised destructor never deletes any pointer, but the user defined ~Foo in the example does delete the member.
Although it's important to learn these things, don't use owning bare pointers in practice. Use RAII classes such as smart pointers or containers instead.
pi stores in stack memory, and it points to an address in heap memory!
you need to call
delete pi;
to deallocate that part of memory in heap
pi itself doesn't need to be deallocated
it will be deallocated after it's scope.
From [basic.life/1]:
The lifetime of an object or reference is a runtime property of the object or reference. A variable is said to have vacuous initialization if it is default-initialized and, if it is of class type or a (possibly multi-dimensional) array thereof, that class type has a trivial default constructor. The lifetime of an object of type T begins when:
storage with the proper alignment and size for type T is obtained, and
its initialization (if any) is complete (including vacuous initialization) ([dcl.init]),
except that if the object is a union member or subobject thereof, its lifetime only begins if that union member is the initialized member in the union ([dcl.init.aggr], [class.base.init]), or as described in [class.union] and [class.copy.ctor], and except as described in [allocator.members].
From [dcl.init.general/1]:
If no initializer is specified for an object, the object is default-initialized.
From [basic.indet/1]:
When storage for an object with automatic or dynamic storage duration is obtained, the object has an indeterminate value, and if no initialization is performed for the object, that object retains an indeterminate value until that value is replaced ([expr.ass]).
[Note 1: Objects with static or thread storage duration are zero-initialized, see [basic.start.static]. — end note]
Consider this C++ program:
int main() {
int i;
i = 3;
return 0;
}
Is initialization performed in the first statement int i; or second statement i = 3; of the function main according to the C++ standard?
I think it is the former, which performs vacuous initialization to an indeterminate value and therefore begins the lifetime of the object (the latter does not perform initialization, it performs assignment to the value 3). If that is so, is it really possible to separate storage allocation from object initialization?
If that is so, is it really possible to separate storage allocation from object initialization?
Yes:
void *ptr = malloc(sizeof(int));
ptr points to allocated storage, but no objects live in that storage (C++20 says that some objects may be there, but nevermind that now). Objects won't exist unless we create some there:
new(ptr) int;
You are getting confused between allocating storage for an object and initializing an object, and they are definitely not the same thing.
In your example, the object i is never initialized. As a local value, it has space reserved for its storage, but it is not initialized with any value.
The second line’s statement assigns a value of 3 to it. This again is not initialization.
Objects with global storage are required by the standard to be both allocated and initialized (to zero or whatever the default initializer does). All other objects are only initialized if the written language construct can support it.
C++ allocators work on this same distinction. The new operator, behind the scenes, both allocates and initializes objects, after which you may assign the object a new value. If you need to, though, you can use the underlying language constructs to manage the two things separately.
For most purposes you do not need to care about the difference between object initialization and assignment in your code. If you get to the point where it matters, you either already know how the concepts differ or need to learn really quickly.
Is initialization performed in the first statement int i; or second statement i = 3; of the function main according to the C++ standard?
The first. The second statement is assignment, not initialization. The second statement marks the point "until that value is replaced ([expr.ass])" from your quotes of the standard.
If [initialization is the first statement] is so, is it really possible to separate storage allocation from object initialization?
Yes, but not in a such a simple example as yours. A common example that comes to mind is a std::vector. Reserving capacity allocates storage space, but that storage is not initialized until an object is added to the vector.
std::vector<int> v; // Allocates and initializes the vector object.
v.reserve(1); // Ensures space has been allocated for an int object.
/*
At this point, the first contained element has space allocated, but has
not yet been initialized. If you want to do nutty things between allocation
and object initialization, this is the place to do it. Note that you are
not allowed to access the allocated space since it belongs to the vector.
You'd have to replicate the inner workings of a vector to do that...
*/
v.emplace_back(3); // Initializes the first contained object.
Quoting the standard
Short version:
There is nothing to quote because the standard does not explicitly prohibit all spurious actions. Compilers avoid spurious actions by their own volition.
Long version:
Strictly speaking, the standard does not guarantee that reserve() does not initialize anything. The requirements imposed on reserve() in [vector.capacity] are more focused on what must be done than on prohibiting spurious activity. The closest it comes to this guarantee is the requirement that the time complexity of reserve() be linear in the size of the container (not in the capacity, but in the current size). This would make it impossible to always initialize everything that was reserved. However, a compiler could still choose to initialize a fixed number of reserved elements, say up to 10 million of them. As long as this limit is fixed, it counts as constant-time complexity, so is allowed by [vector.capacity].
Now let's get real. Compilers are designed to produce fast code, without introducing unnecessary, useless busywork. Compilers do not seek out the possibility of doing additional work simply because the standard does not prohibit it. Except for debug builds, no compiler is going to introduce an initialization when it it not required. The people who view the possibility as something worth considering are language lawyers who lose sight of the big picture. You don't pay for what you don't need. The question to ask here is not "Could you quote the standard supporting that no initialization happens?" but "Could you quote the standard supporting that no initialization is required?" Since the additional work of initialization is not required, it will not happen in practice.
Still, reality means little to some language lawyers, and this question does have that tag. To be thorough, I will demonstrate that it is "possible to separate storage allocation from object initialization" even if you happen to use a pathological, yet standards-compliant, compiler that was over-engineered by masochists. I need only one case to demonstrate "possible", so let's abandon int for a more bizarre, yet fully legal, type.
The sole precondition for reserve() is that the contained type can be move-inserted into the container. This precondition is satisfied by the following class.
class C {
// Default construction is not supported.
C() = delete;
public:
// Move construction is allowed, even outside this class.
C(C &&) = default;
};
I have designed this class to be rather hard to initialize. The only allowed construction is move-construction; in order to initialize an object of this type, you need to already have an object of this type. Who creates the first object? No one. No objects of this type can exist. However, one can still create a vector of these objects (an empty vector, but still a vector).
It is legal to define std::vector<C> v;, and to follow that by a call to v.reserve(1);. This allocates space (1 byte is needed on my system) for an object of type C, and yet there is no possible initialization of this object. QED.
If I have a
class A
{
private:
Widget* widgets[5];
};
Is it guaranteed that all pointers are NULL, or do I need to initialize them in the constructor? Is it true for all compilers?
Thanks.
The array is not initialized unless you do it. The standard does not require the array to be initialized.
It is not initialized if it is on the stack or using the default heap allocator (although you can write your own to do so).
If it is a global variable it is zero filled.
This is true for all conformant compilers.
It depends on the platform and how you allocate or declare instances of A. If it's on the stack or heap, you need to explicitly initialize it. If it's with placement new and a custom allocator that initializes memory to zero or you declare an instance at file scope AND the platform has the null pointer constant be bitwise zero, you don't. Otherwise, you should.
EDIT: I suppose I should have stated the obvious which was "don't assume that this happens".
Although in reality the answer is "it depends on the platform". The standard only tells you what happens when you initialize explicitly or at file scope. Otherwise, it is easiest to assume that you are in an environment that will do the exact opposite of what you want it to do.
And if you really need to know (for educational or optimizational purposes), consult the documentation and figure out what you can rely on for that platform.
In general case the array will not be initialized. However, keep in mind that the initial value of the object of class type depends not only on how the class itself is defined (constructor, etc), but might also depend on the initializer used when creating the object.
So, in some particular cases the answer to your question might depend on the initializer you supply when creating the object and on the version of C++ language your compiler implements.
If you supply no initializer, the array will contain garbage.
A* pa = new A;
// Garbage in the array
A a;
// Garbage in the array
If supply the () initializer, in C++98 the array will still contain garbage. In C++03 however the object will be value-initialized and the array will contain null-pointers
A* pa = new A();
// Null-pointers in the array in C++03, still garbage in C++98
A a = A();
// Null-pointers in the array in C++03, still garbage in C++98
Also, objects with static storage duration are always zero-initialized before any other initialization takes place. So, if you define an object of A class with static storage duration, the array will initially contain null-pointers.
What types in C++ can be instantiated?
I know that the following each directly create a single instance of Foo:
Foo bar;
Foo *bizz = new Foo();
However, what about with built-in types? Does the following create two instances of int, or is instance the wrong word to use and memory is just being allocated?
int bar2;
int *bizz2 = new int;
What about pointers? Did the above example create an int * instance, or just allocate memory for an int *?
Would using literals like 42 or 3.14 create an instance as well?
I've seen the argument that if you cannot subclass a type, it is not a class, and if it is not a class, it cannot be instantiated. Is this true?
So long as we're talking about C++, the only authoritative source is the ISO standard. That doesn't ever use the word "instantiation" for anything but class and function templates.
It does, however, use the word "instance". For example:
An instance of each object with automatic storage duration (3.7.2) is associated with each entry into its block.
Note that in C++ parlance, an int lvalue is also an "object":
The constructs in a C++ program create, destroy, refer to, access, and manipulate objects. An object is a region of storage.
Since new clearly creates regions of storage, anything thus created is an object, and, following the precedent of the specification, can be called an instance.
As far as I can tell, you're really just asking about terminology here. The only real distinction made by the C++ standard is POD types and non-POD types, where non-POD types have features like user-defined constructors, member functions, private variables, etc., and POD types don't. Basic types like int and float are of course PODs, as are arrays of PODs and C-structs of PODs.
Apart from (and overlapping with) C++, the concept of an "instance" in Object-Oriented Programming usually refers to allocating space for an object in memory, and then initializing it with a constructor. Whether this is done on the stack or the heap, or any other location in memory for that matter, is largely irrelevant.
However, the C++ standard seems to consider all data types "objects." For example, in 3.9 it says:
"The object representation of type T
is the sequence of N unsigned char
objects taken up by the object of type
T, where N equals sizeof(T)..."
So basically, the only distinction made by the C++ standard itself is POD versus non-POD.
in C++ an 'instance' and 'instantiate' is only associated with Classes
note however that these are also english words that can have conversational meaning.
'pointer' is certainly a class of things in the english usage and a pointer is certainly an instance of that class
but in c++ speak 'pointer' is not a Class and a pointer is not an Instance of a Class
see also - how many angels on pinheads
The concept of an "instance" isn't something that's really intrinsic to C++ -- basically you have "things which have a constructor and things which don't".
So, all types have a size, e.g. an int is commonly 4 bytes, a struct with a couple of ints is going to be 8 and so on. Now, slap a constructor on that struct, and it starts looking (and behaving) like a class. More specifically:
int foo; // <-- 4 bytes, no constructor
struct Foo
{
int foo;
int bar;
}; // <-- 8 bytes, no constructor
struct Foo
{
Foo() : foo(0), bar(0) {}
int foo;
int bar;
}; // <-- 8 bytes, with constructor
Now, you any of these types can live on the stack or on the heap. When you create something on the stack, like the "int foo;" above, goes away after its scope goes away (e.g. at the end of the function call). If you create something with "new" it goes on the heap and gets its own place to live in memory until you call delete on it. In both cases the constructor, if there, will be called during instantiation.
It is unusual to do "new int", but it's allowed. You can even pass 0 or 1 arguments to the constructor. I'm not sure if "new int()" means it's 0-initialized (I'd guess yes) as distinct from "new int".
When you define a value on the stack, it's not usually called "allocating memory" (although it is getting memory on the stack in theory, it's possible that the value lives only in CPU registers).
Literals don't necessarily get an address in program memory; CPU instructions may encode data directly (e.g. put 42 into register B). Probably arbitrary floating point constants have an address.
In C code like such:
{
int i = 5;
/* ....... */
}
The compiler will replace the code by moving the Stack pointer down (for stacks growing down) by the size of an int, and places the value 5 in that memory place.
Similarly, in C++ code, what does the compiler do if an object is created? For example:
class b
{
public :
int p;
virtual void fun();
};
main()
{
b obj;
}
What will the compiler do for the above code? Can anyone explain when memory is allocated, and when memory for the virtual table is allocated, and when the default constructor is called?
On Constructions
Logically there is no difference between the two:
In both case the stack is made large enough to hold the obect and the constructor is called on the object.
Just note:
The constructor for a POD type does nothing.
A user defined type with no constructor has a compiler generated default cosntructor.
You can think about it like this:
int x; // stack frame increased by sizeof(int) default construct (do nothing)
B a; // stack frame increased by sizeof(B) default construct.
While:
int y(6); // stack frame increased by sizeof(int) Copy constructor called
B b(a); // stack frame increased by sizeof(B) Copy constructor called
Ok. Of course the constructor for POD types is very trivial and the compiler will do a lot of optimizations (and may all but remove any actual code and even the memory address), but logically it is just fine to think of it happining this way.
Note: All types have a copy constructor (the compiler defines one if you don't) and the POD types you can logically think of it as copy construcion without any problems.
As for virtual tables:
Let me first note this is an implementation detail and not all compilers use them.
But the vtable itself is usually generated at compile time. Any object that needs a vtable has an invisable pointer added to the structure (this is included as part of the objects size). Then during contruction the pointer is set to point at the vtable.
Note: It is impossable to define when the vtable is set as this is not defined by the standard and thus each compiler is free to do it at any time. If you have a multiple level hierarchy then the vtable is probably set by each constructor from base to most derived and thus probably wrong until the final constructor finishes.
Note: You can not call virtual functions in the constructor/destructor. So all you can say is that the vtable will be correctly initialised only after the constructor has fully completed.
It's semantically the same, the stack pointer gets decremented (for stacks growing down) by sizeof b, then the default constructor is called to set up your instance.
In practice, depending on your architecture and on your compiler (and the flags you pass to it), basic types like in your int example may not get allocated actual memory space on the stack unless it's really required. They'll live in registers until an operation requiring a real memory address is needed (like the & operator).
To touch on question about when the virtual table get's allocated. Usually it does done at compile time (though it does depend on the compiler).
The virtual table is static for any given class. Because of this the compiler can emit the table at compile time. During the initialization of the class the pointer to the virtual table is set to the stored table.
Because of this different instances of the same class will point to the same virtual table.
On
b obj;
as with an int, the stack pointer is increased by the size of b. Then the constructor is called. The constructor may or may not call new or any other function to allocate memory. Thats up to the b's implementation. The call itself does not initiate any allocation.
The vftable is a static object. It is not created with the object itself. The object rather contains a 'invisible' pointer that points to its matching vftable. Its size is include in sizeof(b).
Just to add to previous answers, once the object is constructed, the compiler will do whatever magic is necessary under it's conventions to guarantee the destructor is called when the object goes out of scope. The language guarantees this, but most compilers have to do something to follow through on the guarantee (like set up a table of pointers to destructors and rules about when to invoke the various destructors for the various objects).
According to Itanium C++ ABI standard (which, for example, GCC follows), virtual table is stored into a separate memory, global to the translation unit.
For each dynamic class a virtual table is constructed and is stored under specific name in the object file, like _ZTV5Class. Classes, whose runtime type is exactly Class will contain pointers to this table. These pointers will be initialized, adjusted and accessed, but no class contains its virtual table within its instance.
So the answer is that virtual tables are allocated at compile time, and during construction only pointers to them are set up.