This may sound naive. I want to know what happens when i explicitly call a constructor like this:
class A{
/*...*/
public:
A(){}
};
int main(){
A();
return 0;
}
Is a useless object created which remains in the memory until the scope of main() ends?
You create an object that lasts until the end of the statement.
Its considered a nameless temporary which gets destroyed after the end of the full expression. In this case, the point right after the semicolon. To prove this, create a destructor with a print statement.
when i explicitly call a constructor like this
You are not calling a constructor here; but creating a temporary object which gets destructed immediately. Constructor can be called explicitly with an object of that type (which is not advisable).
Is a useless object created which remains in the memory until the
scope of main() ends?
It doesn't have scope till the function ends, but till the ; ends.
Strictly speaking you can never make a direct call to a constructor in C++. A constructor is called by the implementation when you cause an object of class type to be instantiated.
The statement A(); is an expression statement and the expression is a degenerate form of an explicit type conversion (functional notation). A refers to the type, strictly speaking constructors don't have names.
From the standard (5.2.3 [expr.type.conv] / 2:
The expression T(), where T is a simple-type-specifier for a non-array complete object type or the (possibly cv-qualified) void type, creates an rvalue of the specified type, which is value-initialized [...].
Because your class type has a user-declared default constructor the value-initialization of this temporary will use this constructor. (see 8.5 [dcl.init]/5)
Okay, I re-visited temporary and found that in the above example, it's actually a part of expression that is initializing an object. So yes, the scope ends at ;
Here:
When a temporary object is created to initialize a reference variable, the name of the temporary object has the same scope as that of the reference variable. When a temporary object is created during the evaluation of a full-expression (an expression that is not a subexpression of another expression), it is destroyed as the last step in its evaluation that lexically contains the point where it was created.
Related
Inspired from this question.
struct E {};
E e;
E f(e); // Accesses e?
To access is to
read or modify the value of an object
The empty class has an implicitly defined copy constructor
The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move of its bases and members. [...] The order of initialization is the same as the order of initialization of bases and members in a user-defined constructor. Let x be either the parameter of the constructor or, for the move constructor, an xvalue referring to the parameter. Each base or non-static data member is copied/moved in the manner appropriate to its type:
[...] the base or member is direct-initialized with the corresponding base or member of x.
I think that the part of the standard that describes the most precisely what performs an access is [basic.life]. In this paragraph it is explained what can be done with a reference that refers to, or a pointer that point to, an object which is out of its lifetime period. Everything that is authorized to do with such entities does not perform an access to the object value since such value does not exist (otherwise the standard would be inconsistent).
So we can take a more drastic example, if this is not undefined behavior, so there are no access to e in your example code (accordingly to the reasonning above):
struct E{
E()=default;
E(const E&){}
};
E e;
e.~E();
E f(e);
Here e is an object whose lifetime has ended but whose storage is still allocated. What can be done with such a lvalue is described in [basic.life]/6
Similarly, before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any glvalue that refers to the original object may be used but only in limited ways. For an object under construction or destruction, see [class.cdtor]. Otherwise, such a glvalue refers to allocated storage ([basic.stc.dynamic.deallocation]), and using the properties of the glvalue that do not depend on its value is well-defined. The program has undefined behavior if:
an lvalue-to-rvalue conversion ([conv.lval]) is applied to such a glvalue,
the glvalue is used to access a non-static data member or call a non-static member function of the object, or
the glvalue is implicitly converted ([conv.ptr]) to a reference to a base class type, or
the glvalue is used as the operand of a static_cast ([expr.static.cast]) except when the conversion is ultimately to cv char& or cv unsigned char&, or
the glvalue is used as the operand of a dynamic_cast ([expr.dynamic.cast]) or as the operand of typeid.
None of the cited point above does happen inside E copy constructor so the example code in this answer is well defined, which implies that there have been no access to the value of the destroyed object. So there is no access to e in your example code.
I think it does not access the object, though a valid object is required to be present.
E f(e);
This calls E's implicitly defined constructor E::E(const E&). Obviously the body of this constructor is empty (because there is nothing to do). So if anything happens, it must happen during argument passing, i.e. during the initialization of const E& from e.
It is self-evident that this initialization does not modify e. Now, to read the value of e, a lvalue-to-rvalue conversion must take place. However, the standard actually says that this conversion does not take place during direct reference binding1. That is to say, no read is performed.
However, the standard does require that a reference must be initialized to refer to a valid object or function2 (though this is subject to CWG 453), so things like E f(*reinterpret_cast<E*>(nullptr)); will be ill-formed.
1. This is done by not normatively requiring such conversion, and further strengthened by the non-normative note in [dcl.init.ref].
2. [dcl.ref].
Looking at the following example. Does the C++ standard guarantee that the value of object.x will be equal to 1 at the end? What if I don't call the destructor object.~Class();?
#include <new>
class Class
{
public:
Class() {}
~Class() {}
int x;
};
int main()
{
Class object;
object.x = 1;
object.~Class();
new (&object) Class();
object.x == ?
Class object_2;
object_2.x = 1;
new (&object_2) Class();
object_2.x == ?
return 0;
}
No.
The object whose x is equal to 1 was destroyed.
You know that, because you're the one who destroyed it.
Your new x is uninitialised and has an unspecified value, which may be 1 in practice due to memory re-use. That's no different than for any other uninitialised value.
Update
Since there seems to be a lot of people throwing about assertions, here are some facts, direct from the standard.
There is no direct statement about this case because it follows from the general rules governing what objects are and what object lifetime means.
Generally, once you've grokked that C++ is an abstraction rather than a direct mapping of bytes, you can understand what's going on here, and why there is no such guarantee as that the OP seeks.
First, some background on object lifetime and destruction:
[C++14: 12.4/5]: A destructor is trivial if it is not user-provided and if:
the destructor is not virtual,
all of the direct base classes of its class have trivial destructors, and
for all of the non-static data members of its class that are of class type (or array thereof), each such class has a trivial destructor.
Otherwise, the destructor is non-trivial.
[C++14: 3.8]: [..] The lifetime of an object of type T ends when:
if T is a class type with a non-trivial destructor (12.4), the destructor call starts, or
the storage which the object occupies is reused or released.
[C++14: 3.8/3]: The properties ascribed to objects throughout this International Standard apply for a given object only during its lifetime. [..]
[C++14: 3.8/4]: A program may end the lifetime of any object by reusing the storage which the object occupies or by explicitly calling the destructor for an object of a class type with a non-trivial destructor. For an object of a class type with a non-trivial destructor, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, if there is no explicit call to the destructor or if a delete-expression (5.3.5) is not used to release the storage, the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior.
(Most of this passage is irrelevant for your first case, as it does have an explicit destructor call: your second violates the rules in this paragraph and thus has undefined behaviour; it shall not be discussed further.)
[C++14: 12.4/2]: A destructor is used to destroy objects of its class type. [..]
[C++14: 12.4/11]: [..] Destructors can also be invoked explicitly.
[C++14: 12.4/15]: Once a destructor is invoked for an object, the object no longer exists. [..]
Now, some specifics. What if we were to inspect object.x before the placement-new?
[C++14: 12.7/1]: [..] For an object with a non-trivial destructor, referring to any non-static member or base class of the object after the destructor finishes execution results in undefined behavior.
Wow, okay.
And what if we were to inspect it after the placement-new? i.e. what value does the x in the new object hold? Is it guaranteed, as the question asks, that it'll be 1? Bear in mind that Class's constructor includes no initialiser for x:
[C++14: 5.3.4/17]: A new-expression that creates an object of type T initializes that object as follows:
If the new-initializer is omitted, the object is default-initialized (8.5); if no initialization is performed, the object has indeterminate value.
Otherwise, the new-initializer is interpreted according to the initialization rules of 8.5 for direct-initialization.
[C++14: 8.5/16]: The initialization that occurs in the forms
T x(a);
T x{a};
as well as in new expressions (5.3.4), static_cast expressions (5.2.9), functional notation type conversions (5.2.3), and base and member initializers (12.6.2) is called direct-initialization.
[C++14: 8.5/17]: The semantics of initializers are as follows. The destination type is the type of the object or reference being initialized and the source type is the type of the initializer expression. If the initializer is not a single (possibly parenthesized) expression, the source type is not defined. [..]
If the initializer is (), the object is value-initialized.
[..]
[C++14: 8.5/8]: To value-initialize an object of type T means:
if T is a (possibly cv-qualified) class type (Clause 9) with either no default constructor (12.1) or a default constructor that is user-provided or deleted, then the object is default-initialized.
[..]
[C++14: 8.5/7]: To default-initialize an object of type T means:
if T is a (possibly cv-qualified) class type (Clause 9), the default constructor (12.1) for T is called (and the initialization is ill-formed if T has no default constructor or overload resolution (13.3) results in an
ambiguity or in a function that is deleted or inaccessible from the context of the initialization);
if T is an array type, each element is default-initialized;
— otherwise, no initialization is performed.
If a program calls for the default initialization of an object of a const-qualified type T, T shall be a class type with a user-provided default constructor.
[C++14: 12.6.2/8]: In a non-delegating constructor, if a given non-static data member or base class is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no
ctor-initializer) and the entity is not a virtual base class of an abstract class (10.4), then:
if the entity is a non-static data member that has a brace-or-equal-initializer, the entity is initialized as specified in 8.5;
otherwise, if the entity is an anonymous union or a variant member (9.5), no initialization is performed;
otherwise, the entity is default-initialized (8.5).
[C++14: 8.5/12]: If no initializer is specified for an object, the object is default-initialized; if no initialization is performed, an object with automatic or dynamic storage duration has indeterminate value.
So, does the standard guarantee that your replacement object's x has value 1? No. It does not.
In practice, why might it not be? Well, any number of reasons. Class's destructor is non-trivial so, per 3.8, the first object's lifetime has ended immediately after you called its destructor.
Technically, the compiler is then free to place an object at that location as long as it's destroyed by the time the placement-new takes effect. There's no reason for it to do so here, but there's nothing prohibiting it; more importantly, a simple { int x = 5; x = 42; } in between the destructor call and the placement-new would be more than entitled to re-use that memory. It's not being used to represent any object at that point!
More realistically, there are implementations (e.g. Microsoft Visual Studio) that, for debug-mode programs, write a recognisable bit-pattern into unused stack memory to aid in diagnosing program faults. There's no reason to think that such an implementation wouldn't hook into destructors in order to do that, and such an implementation would be overwriting your 1 value. There is nothing in the standard prohibiting this whatsoever.
Indeed, if I replace the ? lines in your code with std::cout statements, so that we actually inspect the values, I get a warning about an uninitialised variable and a value 0 in the case where you used a destructor:
main.cpp: In function 'int main()':
main.cpp:19:23: warning: 'object.Class::x' is used uninitialized in this function [-Wuninitialized]
std::cout << object.x << '\n';
^
0
1
Live demo
I'm unsure how much more proof you need that the standard does not guarantee a 1 value here.
Trying to understand c++ string a little more here. Compiler here is g++ 4.7.3.
Question 1: In the following code snippet, will the compiler be smart enough to free the user data (in the heap, implicitly pointed to by s) at the end of the function? I think the answer is yes, just want to confirm.
void dummy() {
string s;
s.append("hello");
//more append
}
Question 2: in the following snippet, compiler will not free the user data pointed to by s when the function returns. Is that right? If so, the user data may be freed in the caller by the compiler (if the caller function itself doesn't return the string object).
string dummy() {
string s;
s.append("hello");
//more append
return s;
}
//some caller
string s1 = dummy();
In question 1, yes the memory for the variable s will be freed when the function dummy ends because that was the enclosing scope of the s variable.
In question 2, the compiler will likely use return value optimization to avoid having to copy the memory from s into s1, since s is about to fall out of scope.
Question 1: In the following code snippet, will the compiler be smart enough to free the user data (in the heap, implicitly pointed to by s) at the end of the function?
Yes, it is required to. At the end of the function scope, the string's destructor will be called, which will free the memory allocated.
The object s is said to have "automatic storage duration", and it's described by the standard via §3.7.3/1:
Block-scope variables explicitly declared register or not explicitly declared static or extern have au- tomatic storage duration. The storage for these entities lasts until the block in which they are created exits.
Question 2: in the following snippet, compiler will not free the user data pointed to by s when the function returns. Is that right? If so, the user data may be freed in the caller by the compiler (if the caller function itself doesn't return the string object).
The compiler may elide the copy there, using what's called RVO (return value optimization). Specifically the standard words this in the following way, in §12.8/31:
This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):
in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv- unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value
in a throw-expression (5.17), when the operand is the name of a non-volatile automatic object (other than a function or catch-clause parameter) whose scope does not extend beyond the end of the innermost enclosing try-block (if there is one), the copy/move operation from the operand to the exception object (15.1) can be omitted by constructing the automatic object directly into the exception object
when a temporary class object that has not been bound to a reference (12.2) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move
when the exception-declaration of an exception handler (Clause 15) declares an object of the same type (except for cv-qualification) as the exception object (15.1), the copy operation can be omitted by treating the exception-declaration as an alias for the exception object if the meaning of the program will be unchanged except for the execution of constructors and destructors for the object declared by the exception-declaration. [ Note: There cannot be a move from the exception object because it is always an lvalue. —endnote]
When object constructed in argument is destructed, before of after function call?
E.g. is the following code safe?
void f(const char*)
{ ... }
std::string g()
{ ... }
...
f(g().c_str());
It always works for me but I don't know is it just undefined behaviour or it actually should work.
g() is a temporary. Lifetime of tempraries extends for the entire time of evaluation of the entire full-expression (in your case, that would be f(g().c_str())) Therefore your usage is safe, unless the f() stores the pointer somewhere.
§12.2/4 There are two contexts in which temporaries are destroyed at a different point than the end of the fullexpression. The first context is when an expression appears as an initializer for a declarator defining an object. In that context, the temporary that holds the result of the expression shall persist until the object’s initialization is complete. [...]
§12.2/5 The second context is when a reference is bound to a temporary. [...]
Neither of these two cases apply in your example.
A temporary object constructed as part of an expression evaluation is destructed after evaluating the full expression containing the expression, unless it is bound to a named reference. (12.2 and 1.9 in the current draft standard are the relevant sections).
So in your example, the temporary constructed to hold the return value of g will be destroyed after f returns.
It always works for me but I don't know is it just undefined behaviour
or it actually should work.
No, there is no undefined behavior because temporary object produced by g() will be deleted after evaluating full expression, that is body of f() function.
C++ Standard n3337 § 12.3/3
When an implementation introduces a temporary object of a class that has a non-trivial constructor (12.1,
12.8), it shall ensure that a constructor is called for the temporary object. Similarly, the destructor shall be
called for a temporary with a non-trivial destructor (12.4). Temporary objects are destroyed as the last step
in evaluating the full-expression (1.9) that (lexically) contains the point where they were created. This is true
even if that evaluation ends in throwing an exception. The value computations and side effects of destroying
C++ Standard n3337 § 12.3/4
There are two contexts in which temporaries are destroyed at a different point than the end of the full-
expression. The first context is when a default constructor is called to initialize an element of an array. If
the constructor has one or more default arguments, the destruction of every temporary created in a default
argument is sequenced before the construction of the next array element, if any.
C++ Standard n3337 § 12.3/5
The second context is when a reference is bound to a temporary. (...)
If I declare an object like this:
void main()
{
myclass objectA(anotherclass(true,true,0));
}
i.e. I create an objectA and another object "anotherclass" by directly calling the latter's constructor, what is "anotherclass"'s scope?
Does it get destructed only when main() finishes?
The temporary gets destructed at the end of the full expression that contains it, i.e. when the call to the constructor of myclass returns.
Per Paragraph 12.2/3 of the C++11 Standard:
Temporary objects are destroyed as the last step
in evaluating the full-expression (1.9) that (lexically) contains the point where they were created. This is true
even if that evaluation ends in throwing an exception. The value computations and side effects of destroying
a temporary object are associated only with the full-expression, not with any specific subexpression.
For this reason, if myclass's constructor takes an argument of type anotherClass by reference (either lvalue reference to const or rvalue reference), it shall not store it for future use, because it will be dangling if a temporary is passed, and dereferencing it would be Undefined Behavior.
It is only objectA that goes out of scope and gets destroyed when returning from the main() function.
The anotherclass object doesn't have a scope. Scope is a property of names, not of objects, and this object is not named. It's just a temporary object and will be destroyed at the end of the full expression.
Here's the definition of scope (§3.3.1):
In general, each particular name is valid only within some possibly discontiguous
portion of program text called its scope.