Skip to content

Instantly share code, notes, and snippets.

@kumaraish
Created June 26, 2014 20:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kumaraish/3a78f58c587e6f8d8995 to your computer and use it in GitHub Desktop.
Save kumaraish/3a78f58c587e6f8d8995 to your computer and use it in GitHub Desktop.
Construction, Move, Copy, Destruction
/* The comments about copy/move operations optimized away (elided) by the compiler are
from compilation using
g++ (tdm64-2) 4.8.1
on a Windows 7 64-bit machine
The behaviour on other systems would be interesting to see.
*/
#include <iostream>
class Integer {
public:
/* Default Constructor
*/
Integer() {
std::cout << "Default Constructor [" << data << "]\n";
}
/* Ordinary one argument constructor
* Acts as an implicit conversion from its argument type to its type
* mark it explicit if implicit conversion is not desired
* e.g., explicit Integer(int i);
*/
Integer(int i) : data {i} {
std::cout << "Ordinary (one arg) Constructor [" << data << "]\n";
}
/* Copy constructor
*/
Integer(const Integer &rhs) : data {rhs.data} {
std::cout << "Copy Constructor [" << data << "]\n";
}
/* Move constructor
*/
Integer(Integer &&rhs) : data {rhs.data} {
std::cout << "Move Constructor [" << data << "]\n";
}
/* Copy Assignment
*/
Integer& operator=(const Integer& rhs) {
data = rhs.data;
std::cout << "Copy Assignment [" << data << "]\n";
}
/* Move Assignment
*/
Integer& operator=(Integer&& rhs) {
data = rhs.data;
std::cout << "Move Assignment [" << data << "]\n";
}
/* Destructor
*/
~Integer() {
std::cout << "Destructor [" << data << "]\n";
}
friend std::ostream& operator<<(std::ostream& o, const Integer& i);
private:
int data;
};
std::ostream& operator<<(std::ostream& o, const Integer& i) {
o << i.data;
}
void message(std::string msg) {std::cout << "----- " << msg << "-----\n";}
void doNothing(Integer arg) {}
Integer ident(Integer arg) { return arg;}
Integer global; // see ++
int main() {
/* Default initialization
The default initialization of using {} is defined as initialization of each member by {}, in case no
constructor is defined by the programmer.
*/
message("Default initialization");
Integer def{}; // This will initialize def to {unknown}, since constructors for Integer are provided.
// See next comment also. If no constructors were provided, def would be initialized to
// default value of int i.e., {0}
/* ++
Where no constructor requiring arguments is declared, it is also possible to leave out the initializer
completely.
For this, the rules are not as clean as we might like. For statically allocated objects (§6.4.2), the
rules are exactly as if you had used {}, so the value of global is {0}. However, for local variables
and free-store objects, the default initialization is done only for members of class type, and members
of built-in type are left uninitialized, so the value of local is {unknown}.
The reason for this complication is to improve performance in rare critical cases.
See TCPL $17.3.1
*/
Integer local;
int* p1 = new int; // *p1 is uninitialized
int* p2 = new int{}; // *p2 == 0
int* p3 = new int{7}; // *p3 == 7
/* explicit or direct Initialization
*/
message("explicit or direct Initialization");
Integer i1 = Integer{1};
Integer i2{2}; // Shortcut for above
doNothing(Integer{12}); // arg is explicity initialized with Integer{12}
/* copy Initialization
An initialization with an = is considered a copy initialization. In principle, a copy of the initializer
is placed into the initialized object. However, such a copy may be optimized away (elided), and a
move operation (§3.3.2, §17.5.2) may be used if the initializer is an rvalue (§6.4.1).
The distinction between direct initialization and copy initialization (§16.2.6) is maintained for {}
initialization.
*/
message("copy Initialization");
Integer i3 = 3; // One arg constructor implicitly converts RHS to an obj of type Integer;
// then LHS should be copy constructed; however, compiler optimizes away that copy operation
// and the statement is executed like
// Integer i3 = Integer{3};
// An explicit one arg constructor will not allow this implicit conversion
Integer i4 = {4}; // {4} is converted to an Integer either by using an std::initializer_list constructor
// or an Ordinary one-arg constructor, in that priority order.
doNothing(13);
doNothing({14});
/* Copy construction
*/
message("Copy construction");
Integer i5 = i4;
doNothing(i5); // arg is copy constructed from i5
/* Move construction
*/
message("Move construction");
ident(i4); // a temporary to hold return value from ident() is move constructed
Integer i6 = ident(i4); // the construction of the temporary (as in previous statement) in this case is
// elided (optimized away) by the compiler and i6 is directly move constructed
// from arg;
/* Copy assignment
*/
message("Copy assignment");
i5 = i4;
/* Move assignment
*/
message("Move assignment");
i5 = ident(i4); // a temporary to hold return value from ident() is move constructed
// i5 is then move assigned the value of that temporary
message("Base and member construction");
struct Real : public Integer {
Integer i;
Real(int a) : Integer{a}, i{a+2} {
std::cout << "Real one arg constructor\n";
}
};
Real r{100};
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment