My Notes Core C++ - Original
My Notes Core C++ - Original
DEFINITION............................................................................................................................................44
INITIALIZATION***..............................................................................................................................46
DEFAULT INITIALIZATION***..................................................................................................................46
VALUE INITIALIZATION***.....................................................................................................................46
COPY INITIALIZATION***.......................................................................................................................46
DIRECT INITIALIZATION***....................................................................................................................46
AGGREGRATE INITIALIZATION***..........................................................................................................46
LIST INITIALIZATION (SINCE C++11) ***...............................................................................................46
REFERENCE INITIALIZATION***..............................................................................................................46
STATIC NON LOCAL INITIALIZATION***..................................................................................................47
ZERO INITIALIZATION***........................................................................................................................47
CONSTANT INITIALIZATION***...............................................................................................................47
DYNAMIC NON LOCAL INITIALIZATION***.............................................................................................47
EXPRESSIONS.........................................................................................................................................48
1
VALUE CATEGORIES...............................................................................................................................48
EXPRESSION PARSER...............................................................................................................................49
OPERATORS.............................................................................................................................................49
Logical operators***........................................................................................................................49
Bitwise operators***........................................................................................................................49
Shift operators...................................................................................................................................50
Write a generic method to convert any unsigned number (int or long) to binary number ???........52
The Conditional operator (ternary operator (?))***.......................................................................52
The Comma Operator***.................................................................................................................53
The Compile-Time Operator sizeof...................................................................................................54
Common operators............................................................................................................................54
Operator Precedence Summary........................................................................................................54
CONVERSIONS.........................................................................................................................................57
CASTING OPERATORS..............................................................................................................................57
C-STYLE CAST.........................................................................................................................................57
C++ EXPLICIT CASTS..............................................................................................................................57
static_cast.........................................................................................................................................58
dynamic_cast.....................................................................................................................................59
const_cast..........................................................................................................................................60
reinterpret_cast.................................................................................................................................60
Run Time Type Identification (RTTI)................................................................................................61
standard conversions***..................................................................................................................62
const_cast conversion.......................................................................................................................62
static_cast conversion.......................................................................................................................62
dynamic_cast conversion..................................................................................................................63
reinterpret_cast conversion...............................................................................................................63
explicit cast conversion....................................................................................................................63
User-defined conversion...................................................................................................................63
NULL-TERMINATED STRINGS***............................................................................................................63
CLASSES...................................................................................................................................................65
ENCAPSULATION.....................................................................................................................................65
ABSTRACTION.........................................................................................................................................65
CLASS TYPES..........................................................................................................................................65
-----------------------------------------------.................................................................................................65
C++ HEADER FILES***...........................................................................................................................66
UNION TYPES..........................................................................................................................................67
CONSTRUCTORS......................................................................................................................................67
Explicit constructor***.....................................................................................................................68
Constructor gotcha***.....................................................................................................................69
Storage allocation for classes...........................................................................................................69
Initialization lists***........................................................................................................................70
DESTRUCTORS........................................................................................................................................71
COPY CONSTRUCTOR???........................................................................................................................71
ASSIGNMENT OPERATOR (COPY ASSIGNMENT OPERATOR) ***..............................................................71
Virtual assignment operator***.......................................................................................................77
COPY CTOR VS ASSIGNMENT OPERATOR................................................................................................84
CANONICAL FORM OF A CLASS...............................................................................................................84
COMPOSITION (CONTAINMENT, AGGREGATION, EMBEDDING, LAYERING)............................................86
FRIENDS..................................................................................................................................................86
Friend functions................................................................................................................................86
Friend classes*** (???)....................................................................................................................87
REDUCING RECOMPILATION***.....................................................................................................89
READ LARGE SCALE C++ SOFTWARE DESIGN.......................................................................................89
???..........................................................................................................................................................89
FUNCTIONS ***......................................................................................................................................90
2
FUNCTION ARGUMENTS & RETURN VALUES...........................................................................................90
DEFAULT VALUES....................................................................................................................................93
FUNCTION PROTOTYPES..........................................................................................................................94
FUNCTION CALLS***..............................................................................................................................94
RESOLUTION OF FUNCTION CALLS***....................................................................................................94
FUNCTION POINTERS AND CALL BACK TECHNIQUE***..........................................................................95
Function pointers..............................................................................................................................95
Defining a function pointer...............................................................................................................95
To define a pointer to a function that has no arguments and no return value, you say:.........95
void (*funcPtr)();..............................................................................................................................95
Complicated examples.............................................................................................................95
//ComplicatedDefinitions.cpp...........................................................................................................95
/* 1. */ void * (*(*fp1)(int))[10];..............................................................................................95
/* 2. */ float (*(*fp2)(int,int,float))(int);...................................................................................95
/* 3. */ typedef double (*(*(*fp3)())[10])();.............................................................................95
fp3 a;.................................................................................................................................................95
/* 4. */ int (*(*f4())[10])();.......................................................................................................95
int main() {}......................................................................................................................................95
Number 1 says “fp1 is a pointer to a function that takes an integer argument and returns a pointer
to an array of 10 void pointers.”......................................................................................................95
Number 2 says “fp2 is a pointer to a function that takes three arguments (int, int, and float) and
returns a pointer to a function that takes an integer argument and returns a float.”......................96
Number 3 says “fp3 is a pointer to a function that takes no arguments and returns a pointer to an
array of 10 pointers to functions that take no arguments and return doubles.”...............................96
Number 4 is a function declaration instead of a variable definition. It says “f4 is a function that
returns a pointer to an array of 10 pointers to functions that return integers.”..............................96
Call back technique...........................................................................................................................99
Using Polymorphism and Virtual Functions Instead of Function Pointers***..............................100
FUNCTION OVERLOADING.....................................................................................................................101
Finding the Address of an Overloaded Function*** (???)............................................................102
FUNCTION OVERRIDING........................................................................................................................102
FUNCTION OVERLOADING VS. OVERRIDING.........................................................................................103
OPERATOR OVERLOADING.............................................................................................................103
WHICH OPERATORS CANNOT BE OVERLOADED AND WHY....................................................................103
RESTRICTIONS.......................................................................................................................................103
WHY FRIENDS IN OPERATOR OVERLOADING.........................................................................................104
OVERLOADING INSERTION AND EXTRACTION OPERATORS (<< AND >>)..............................................106
OVERLOADING SUBSCRIPT OPERATOR..................................................................................................106
OVERLOADING FUNCTION CALL OPERATOR***....................................................................................106
TYPE CONVERSION OPERATORS***......................................................................................................107
INHERITANCE & COMPOSITION***.............................................................................................109
PUBLIC, PRIVATE AND PROTECTED INHERITANCE..................................................................................112
Public inheritance...........................................................................................................................112
Protected inheritance......................................................................................................................113
Private inheritance..........................................................................................................................113
MULTILEVEL AND MULTIPLE INHERITANCES.........................................................................................114
Multlevel Inheritance......................................................................................................................114
Multiple Inheritance........................................................................................................................115
Virtual inheritance***.....................................................................................................................116
Virtual functions..............................................................................................................................117
TEMPLATES..........................................................................................................................................117
EXPLICIT INITIALIZATION FOR FUNDAMENTAL TYPES..........................................................................118
TEMPLATE PARAMETERS AND TEMPLATE ARGUMENTS.........................................................................118
Non-type template parameters/arguments......................................................................................119
3
Template type arguments.................................................................................................................122
Template template arguments.........................................................................................................122
Default argument for template parameter......................................................................................123
CLASS TEMPLATES................................................................................................................................124
Template Static Data Member.........................................................................................................125
Using Non-type (standard) parameters..........................................................................................126
Using Default arguments with template classes.............................................................................126
Nested Template Classes.................................................................................................................127
Class template deduction***..........................................................................................................127
FUNCTION TEMPLATE............................................................................................................................127
Instantiation of function template...................................................................................................127
Template argument deduction.........................................................................................................129
Explicit template arguments............................................................................................................129
Template argument substitution......................................................................................................130
Function template specialization???..............................................................................................131
Function template overloading???.................................................................................................132
CLASS MEMBER TEMPLATE..................................................................................................................134
Member function templates.............................................................................................................135
Conversion function templates (Templates and automatic type conversions)................................137
Member variable template..............................................................................................................140
Template constructors.....................................................................................................................141
TEMPLATE SPECIALIZATION..................................................................................................................143
Explicit (full) template specialization.............................................................................................143
Partial template specialization.......................................................................................................148
FUNCTION TEMPLATE OVERLOADING AND SPECIALIZATION EXAMPLES..............................................153
FRIEND AND TEMPLATES.......................................................................................................................154
Templates friends............................................................................................................................154
Template friend operators...............................................................................................................156
TEMPLATES AND MULTIPLE-FILE PROJECTS..........................................................................................158
EXCEPTION HANDLING....................................................................................................................158
FUNDAMENTALS...................................................................................................................................159
WHAT TO THROW..................................................................................................................................159
WHAT TO CATCH...................................................................................................................................160
CATCH THROUGH INHERITANCE............................................................................................................160
EXCEPTION SPECIFICATION...................................................................................................................161
STACK UNWINDING..............................................................................................................................161
EXCEPTION THROWN FROM CONSTRUCTOR AND DESTRUCTOR...........................................................162
INSUFFICIENT MEMORY WHEN USING NEW..........................................................................................163
STANDARD LIBRARY EXCEPTION HIERARCHY.....................................................................................163
MFC WAY OF EXCEPTION HANDLING***..............................................................................................165
INPUT/OUTPUT USING STREAM CLASSES (WORK ON REDUCING THE MATERIAL)...166
STANDARD FUNCTION LIBRARY...................................................................................................167
THE C-BASED I/O FUNCTIONS.............................................................................................................167
STRING AND CHARACTER FUNCTIONS..................................................................................................167
MATHEMATICAL FUNCTIONS.................................................................................................................167
TIME DATE AND LOCALIZATION FUNCTIONS........................................................................................167
DYNAMIC ALLOCATION FUNCTIONS.....................................................................................................167
(ONLY DYNAMIC MEMORY MANAGEMENT)..........................................................................................167
UTILITY FUNCTIONS.............................................................................................................................167
WIDE-CHARACTER FUNCTIONS............................................................................................................167
The Wide-Character Classification Functions................................................................................167
The Wide-Character I/O Functions................................................................................................167
SPECIAL TOPICS..................................................................................................................................168
C++ CODING STYLE IDIOMS???...........................................................................................................168
4
RAII: Resource Acquisition Is Initialization...................................................................................168
Copy-swap Idiom............................................................................................................................168
CRTP: Curiously Recurring Template Pattern...............................................................................168
pImpl: Pointer-to-Implementation..................................................................................................168
Compile-time polymorphism...........................................................................................................168
Template and Hook.........................................................................................................................168
THE PROBLEM WITH CONST DATA MEMBERS???.................................................................................168
CLONING C++ CLASS WITH PURE VIRTUAL METHODS..........................................................................168
MEMCPY IS EVIL....................................................................................................................................169
USING THE ASM KEYWORD???.............................................................................................................170
LINKAGE SPECIFICATION****MOVE FROM HERE***..........................................................................170
External linkage..............................................................................................................................170
Internal linkage...............................................................................................................................170
No linkage.......................................................................................................................................171
ARRAY-BASED I/O???..........................................................................................................................171
AUTO_PTRS...........................................................................................................................................171
PROXY CLASSES (SURROGATES)???......................................................................................................180
REFERENCE COUNTING???....................................................................................................................182
SOFTWARE ENGINEERING WITH INHERITANCE.....................................................................183
FINALLY SOME IMPORTANT POINTS...........................................................................................183
PREPROCESSOR..................................................................................................................................184
PREPROCESSOR MACROS (#DEFINE, #UNDEF).......................................................................................184
FUNCTION MACROS...............................................................................................................................184
CONDITIONAL INCLUSIONS (#IFDEF, #IFNDEF, #IF, #ENDIF, #ELSE AND #ELIF)....................................185
CONDITIONAL COMPILATION................................................................................................................186
LINE CONTROL (#LINE)........................................................................................................................186
ERROR DIRECTIVE (#ERROR)................................................................................................................186
SOURCE FILE INCLUSION (#INCLUDE)***.............................................................................................187
PRAGMA DIRECTIVE (#PRAGMA)..........................................................................................................188
PREDEFINED MACRO NAMES.................................................................................................................188
WHY ARE PREPROCESSOR MACROS EVIL..............................................................................................190
PREPROCESSOR PITFALLS***................................................................................................................190
???...................................................................................................................................................190
INLINE FUNCTIONS................................................................................................................................190
MACRO VS INLINE FUNCTIONS.............................................................................................................191
THE C ASSERT( ) MACRO......................................................................................................................192
NICE C++ FAQS.....................................................................................................................................194
Why doesn't overloading work for derived classes?.......................................................................194
Is there a "placement delete"?........................................................................................................195
Why can't I define constraints for my template parameters?.........................................................196
Why can't I assign a vector<Apple*> to a vector<Fruit*>?.........................................................198
How do I read a string from input?................................................................................................199
Why can't I overload dot, ::, sizeof, etc.?........................................................................................199
How do I convert an integer to a string?........................................................................................200
How do I call a C function from C++?..........................................................................................201
Should I put "const" before or after the type?................................................................................202
What good is static_cast?...............................................................................................................202
So, what's wrong with using macros?.............................................................................................203
Is there is any reason to make the permissions on an overridden C++ virtual function different
from the base class? Is there any danger in doing so?...................................................................204
What happens when you make call "delete this?............................................................................205
How virtual functions are implemented C++?...............................................................................206
Can you think of a situation where your program would crash without reaching the breakpoint
which you set at the beginning of main()?......................................................................................206
5
What does extern "C" int func(int *, Foo) accomplish?.................................................................206
What are storage qualifiers in C++?.............................................................................................206
MORE NICE C++ FAQS (FROM C++ FAQS BOOK - MARSHALL CLINE)...............................208
???........................................................................................................................................................208
BRAINBENCH LIKE QUESTIONS....................................................................................................209
EXTERN.................................................................................................................................................209
DECLARATION.......................................................................................................................................209
SCOPE EXCEPTION................................................................................................................................210
SCOPE-IF...............................................................................................................................................210
CLASS SCOPE........................................................................................................................................211
ENUM DECLARATIONS...........................................................................................................................212
NAMESPACE LOOKUP............................................................................................................................213
LINKAGE...............................................................................................................................................213
STATIC STORAGE...................................................................................................................................214
STANDARD CONVERSIONS....................................................................................................................214
TEMPORARIES.......................................................................................................................................215
CASTS...................................................................................................................................................215
DELETE..................................................................................................................................................216
POINTERS COMPARISON........................................................................................................................217
CONDITIONAL OPERATOR......................................................................................................................217
ARRAY DECLARATION...........................................................................................................................218
OVERLOADING......................................................................................................................................218
DEFAULT ARGUMENT............................................................................................................................218
INITIALIZATION.....................................................................................................................................220
VIRTUAL FUNCTIONS............................................................................................................................221
ACCESS CONTROL................................................................................................................................223
USER DEFINED CONVERSIONS...............................................................................................................223
MEMBER INITIALIZATION......................................................................................................................223
COPYING OBJECTS................................................................................................................................224
FUNCTION OVERLOADING....................................................................................................................225
MEMBER TEMPLATES............................................................................................................................226
VALID DECLARATION FOR MAIN...........................................................................................................227
EXERCISES............................................................................................................................................228
FROM “C++ ESSENTIALS”???...............................................................................................................228
FROM “THINKING IN C++ VOLUME 1”???...........................................................................................228
From “Thinking in C++ Volume 2”???................................................................................................228
6
Intetionally left blank
7
8
9
Basics Concepts
main ( )
Below operations are performed before the main function is called at program
startup
Parse GetCommandLine() to argc and argv
Standard C Library initialization
C++ Constructor of global variables
The above execution may be different on different platforms and therefore it may
not be safe to call standard C functions inside C++ constructor of global variables
The main () function returns an integer to the calling process, which is generally the
operating system. Returning a value from main () is the equivalent of calling exit ()
with the same value.
If main () does not explicitly return a value, the value passed to the calling process
is technically undefined. In practice, most C/C++ compilers automatically return 0,
but do not rely on this if portability is a concern.
C and C++ have a special argument list for main( ), which looks like this:
The first argument is the number of elements in the array, which is the second
argument. The second argument is always an array of char*, because the arguments
are passed from the command line as character arrays. Each whitespace-delimited
cluster of characters on the command line is turned into a separate array argument.
The following program prints out all its command-line arguments by stepping
through the array:
//CommandLineArgs.cpp
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
cout << "argc = " << argc << endl;
for(int i = 0; i < argc; i++)
cout << "argv[" << i << "] = "
<< argv[i] << endl;
}
All you get from the command-line is character arrays; if you want to treat an
argument as some other type, you are responsible for converting it inside your
program.
10
To facilitate the conversion to numbers, there are some helper functions in the
Standard C library, declared in <cstdlib>. The simplest ones to use are atoi( ),
atol( ), and atof( ) to convert an ASCII character array to an int, long, and double
floating-point value, respectively.
int main ()
{
int m;
m= FindMax (7,702,422,631,834,892,104,772);
printf ("The largest value is: %d\n",m);
return 0;
}
11
Declarations
A declaration introduces a name – an identifier – to the compiler
Declaration indicates type
Can be repeated (one exception)
Type
Objects, references, functions including function template specializations, and
expressions have a property called type, which both restricts the operations that are
permitted for those entities and provides semantic meaning to the otherwise generic
sequences of bits.
Data types define the way you use storage (memory) in the programs you write.
By specifying a data type, you tell the compiler how to create a particular piece of
storage, and also how to manipulate that storage
Type classification
fundamental types (see also std::is_fundamental):
the type void (see also std::is_void);
the type std::nullptr_t (since C++11) (see also std::is_null_pointer);
arithmetic types (see also std::is_arithmetic):
floating-point types (float, double, long double) (see also
std::is_floating_point);
integral types (see also std::is_integral):
the type bool;
character types:
narrow character types (char, signed char, unsigned char);
wide character types (char16_t, char32_t, wchar_t);
signed integer types (short int, int, long int, long long int);
unsigned integer types (unsigned short int, unsigned int, unsigned long int,
unsigned long long int);
compound types (see also std::is_compound):
reference types (see also std::is_reference):
i. lvalue reference types (see also std::is_lvalue_reference):
1. lvalue reference to object types;
2. lvalue reference to function types;
ii. rvalue reference types (see also std::is_rvalue_reference):
1. rvalue reference to object types;
2. rvalue reference to function types;
pointer types (see also std::is_pointer):
i. pointer to object types;
ii. pointer to function types;
pointer to member types (see also std::is_member_pointer):
i. pointer to data member types (see also
std::is_member_object_pointer);
ii. pointer to member function types (see also
std::is_member_function_pointer);
array types (see also std::is_array);
12
function types (see also std::is_function);
enumeration types (see also std::is_enum);
class types:
i. non-union types (see also std::is_class);
ii. union types (see also std::is_union).
For every type other than reference and function, the type system supports three
additional cv-qualified versions of that type (const, volatile, and const volatile).
Types are grouped in various categories based on their properties:
object types: any (possibly cv-qualified) type other than function, reference,
or void types: arithmetic, pointer, pointer to member, array, enumeration,
class, and std::nullptr_t types (that is, a type that an object may have) (see
also std::is_object);
scalar types: (possibly cv-qualified) arithmetic, pointer, pointer to member,
enumeration, and std::nullptr_ttypes (see also std::is_scalar);
trivial types (see also std::is_trivial), POD types (see also std::is_pod), literal
types (see also std::is_literal_type), and other categories listed in the the
type traits library or as named type requirements.
Type naming
A name can be declared to refer to a type by means of:
class declaration;
enum declaration;
typedef declaration;
type alias declaration.
Example:
Fundamental types
A built-in data type is one that the compiler intrinsically understands.
13
C++ doesn’t say how many bits each of the built-in types must contain. Instead, it
stipulates the minimum and maximum values that the built-in type must be able to
hold. When a machine is based on binary, this maximum value can be directly
translated into a minimum number of bits necessary to hold that value.
The minimum and maximum values that can be stored in the various data types are
defined in the system header files limits.h and float.h (in C++ you will generally
#include <climits> and <cfloat> instead).
C++ have these basic built-in data types
void : - type with an empty set of values. It is an incomplete type that cannot
be completed (consequently, objects of type void are disallowed). There are
no arrays of void, nor references to void. However, pointers to void and
functions returning type void (procedures in other languages) are permitted.
std::nullptr_t:- std::nullptr_t is the type of the null pointer literal, nullptr. It is
a distinct type that is not itself a pointer type or a pointer to member type.
bool :- bool type is capable of holding one of the two values: true or false.
The value of sizeof(bool) is implementation defined and might differ from
1.
Character types
i. signed char - type for signed character representation.
ii. unsigned char - type for unsigned character representation. Also used
to inspect object representations (raw memory).
iii. char - type for character representation which can be most efficiently
processed on the target system (has the same representation and
alignment as either signed char or unsigned char, but is always a
distinct type). Multibyte characters strings use this type to represent
code units. The character types are large enough to represent 256
different values (in order to be suitable for storing UTF-8 encoded
data) (since C++14)
iv. wchar_t - type for wide character representation (see wide strings).
Required to be large enough to represent any supported character
code unit (32 bits on systems that support Unicode. A notable
exception is Windows, where wchar_t is 16 bits) It has the same size,
signedness, and alignment as one of the integral types, but is a
distinct type.
v. char16_t - type for UTF-16 character representation, required to be
large enough to represent any UTF-16 code unit (16 bits). It has the
same size, signedness, and alignment as std::uint_least16_t, but is a
distinct type.
vi. char32_t - type for UTF-32 character representation, required to be
large enough to represent any UTF-32 code unit (32 bits). It has the
same size, signedness, and alignment as std::uint_least32_t, but is a
distinct type.
int – Its basic integer type. The keyword int may be omitted if any of the
modifiers listed below are used. If no length modifiers are present, it's
guaranteed to have a width of at least 16 bits. However, on 32/64 bit systems
14
it is almost exclusively guaranteed to have width of at least 32 bits (see
below).
Modifiers: - modifies the integer type. Can be mixed in any order. Only one
of each group can be present in type name.
i. signed - target type will have signed representation (this is the
default if omitted)
ii. unsigned - target type will have unsigned representation
iii. short - target type will be optimized for space and will have width of
at least 16 bits.
iv. long - target type will have width of at least 32 bits.
v. long long - target type will have width of at least 64 bits.
Note: as with all type specifiers, any order is permitted: unsigned long long
int and long int unsigned long name the same type.
Floating point types
i. float - single precision floating point type. Usually IEEE-754 32 bit
floating point type
ii. double - double precision floating point type. Usually IEEE-754 64
bit floating point type
iii. long double - extended precision floating point type. Does not
necessarily map to types mandated by IEEE-754. Usually 80-bit x87
floating point type on x86 and x86-64 architectures.
Floating-point types may support special values:
i. infinity (positive and negative), see INFINITY
ii. the negative zero, -0.0. It compares equal to the positive zero, but
is meaningful in some arithmetic operations,
e.g. 1.0/0.0 == INFINITY, but 1.0/-0.0 == -INFINITY), and for
some mathematical functions, e.g. sqrt(std::complex)
iii. not-a-number (NaN), which does not compare equal with anything
(including itself). Multiple bit patterns represent NaNs,
see std::nan, NAN. Note that C++ takes no special notice of
signalling NaNs other than detecting their support
by std::numeric_limits::has_signaling_NaN, and treats all NaNs as
quiet.
15
Besides the minimal bit counts, the C++ Standard guarantees that
Note: this allows the extreme case in which bytes are sized 64 bits, all types
(including char) are 64 bits wide, and sizeof returns 1 for every type
Range of values
16
Object Lifetime
Lifetime of an object is equal to or is nested within the lifetime of its storage. See
Lifetime of a reference is exactly its storage duration.
The lifetime of a reference begins when its initialization is complete and ends as if
it were a scalar object.
Note that the lifetime of the referred object may end before the end of the lifetime
of the reference, which makes dangling references possible.
Lifetimes of member objects and base subobjects begin and end following
Initialization lists***.
Temporary objects are created when a prvalue is materialized so that it can be used
as a glvalue, which occurs in the following situations:
binding a reference to a prvalue
returning a prvalue from a function
conversion that creates a prvalue
lambda expression, (since C++11)
copy-initialization that requires conversion of the initializer,
list-initialization that constructs an std::initializer_list, (since C++11)
Reference-initialization to a different but convertible type or to a bitfield.
(until C++17)
when initializing an object of type std::initializer_list<T> from a braced-
init-list
17
when performing member access on a class prvalue
when performing an array-to-pointer conversion or subscripting on an array
prvalue
for unevaluated operands in sizeof and typeid
when a prvalue appears as a discarded-value expression
if supported by the implementation, when passing or returning an object of
trivially-copyable type in a function call expression (this models passing
structs in CPU registers)
All temporary objects are destroyed as the last step in evaluating the full-
expression that (lexically) contains the point where they were created, and if
multiple temporary objects were created, they are destroyed in the order
opposite to the order of creation. This is true even if that evaluation ends in
throwing an exception. There are two exceptions from that:
The lifetime of a temporary object may be extended by binding to a const
lvalue reference or to an rvalue reference (since C++11), see reference
initialization for details.
The lifetime of a temporary object created when evaluating the default
arguments of a default constructor used to initialize an element of an array
ends before the next element of the array begins initialization.
Namespace***
http://en.cppreference.com/w/cpp/language/namespace
18
using <NamespaceName>::<typeName>
Example:
//UsingDeclaration.h
#ifndef USINGDECLARATION_H
#define USINGDECLARATION_H
namespace U
{
inline void f() {}
inline void g() {}
}
namespace V
{
inline void f() {}
inline void g() {}
}
#endif // USINGDECLARATION_H ///:~
//UsingDeclaration1.cpp
#include "UsingDeclaration.h"
void h()
{
using namespace U; // Using directive
using V::f; // Using declaration
f(); // Calls V::f();
U::f(); // Must fully qualify to call
}
int main() {}
3+4 //rvalue
a+b //rvalue
a++ //rvalue
a=b //OK
(a = b) = 111 //OK in C++ but not in 'C'
More Examples: -
++++a; //OK as it results in ++(++a); which means ++a is becoming
//an lvalue which is perfectly fine
19
a++++; //NO as it results in (a++)++ which means a++ is becoming
//an lvalue which is not allowed
Pointers
In this scenario
The value of x can neither be changed using its name nor using p.
(x = 20; *p = 20;) //Error
As pointer is not constant so p can be used to point to some other constant
variable.
(const int y = 10; p = &y;) //OK
*p = 111; //Error
a = 111; //OK
20
Constant pointer to a constant variable
Example
(const int x = 10; const int* const p(&x);)
In this scenario
The value of x can neither be changed using its name nor using p.
Also p can not be used to point to some other const variable.
A Constant pointer has to be initialized
Example
int* const q; //Error
int x(30), y(40);
Arrays***
The identifier of an array is unlike the identifiers for ordinary variables. For one
thing, an array identifier is not an lvalue; you cannot assign to it. So one way to
look at the array identifier is as a read-only pointer to the beginning of an array.
If you declare an array as a function argument, what you’re really declaring is a
pointer so the below two declaration of func1 and func2 are identical
21
The following is from section A8.7 of "The C Programming Language" by
K&R, 2nd edition, pages 219,220
An aggregate is a structure or array. If an aggregate contains members of aggregate
type, the initialization rules apply recursively. Braces may be elided in the
initialization as follows: if the initializer for an aggregate's member that is itself
an aggregate begins with a left brace, then the succeeding comma-separated list
of initializers initialize the members of the sub aggregate; it is erroneous for there
to be more initializers than members. If, however, the initializer for a sub
aggregate does not begin with a left brace, then only enough elements from the
list are taken to account of the members of the sub aggregate; any remaining
members are left to initialize the next member of the aggregate of which the sub
aggregate is a part. For example,
int x[] = { 1, 3, 5 };
Declares and initializes x as a 1-dimensional array with thee members, since no size
was specified and there are three initializers.
float y[4][3] = {
{ 1 }, { 2 }, { 3 }, { 4 }
};
Initializes the first column of y and leaves the rest 0.
So the compiler doesn't ignore the inner braces. However, the inner braces are
optional if you specify all of the initializers in order with no gaps. Using the inner
braces gives you more control over the initialization, if you don't want to specify a
full set of initializers.
22
{
public:
int a[2][2];
A();
};
A::A()
{
a = {{1,2},{2,4}};
}
yields: error: assigning to an array from an initializer list
class A
{
public:
int a[2][2];
A();
};
A::A()
{
int b[2][2] = {{1,2},{2,4}};
a = b;
}
yields: invalid array assignment
class A
{
public:
int **a;
A();
};
A::A()
{
int b[2][2] = {{1,2},{2,4}};
a = b;
}
yields: cannot convert ‘int [2][2]’ to ‘int**’ in assignment
class A
{
public:
int a[2][2];
A();
23
};
A::A()
{
int at[2][2] = {{1,2},{2,4}};
*(array_t*)a = *(array_t*)at;
}
We can always use “Black” instead of 0, but the compiler will just treat “Black”
as 0; Now “Color” is a user-defined type.
If you give an enum type a name, you can use that type for variables, function
arguments and return values, and so on:
enum MyEnumType x; //legal in both C and C++
MyEnumType y; // legal only in C++
24
Example:
enum MyEnumType { ALPHA, BETA, GAMMA };
int i = BETA; // give i a value of 1
int j = 3 + GAMMA; // give j a value of 5
On the other hand, there is not an implicit conversion from int to an enum type:
MyEnumType x = 2; //should NOT be allowed by compiler
MyEnumType y = 123; //should NOT be allowed by compiler
In addition, there’s stricter type checking for enumerations in C++. If you have
an instance of an enumeration color called a. You can’t say a++. This is because
incrementing an enumeration is performing two type conversions, one of them
legal in C++ and one of them illegal
http://en.cppreference.com/w/cpp/language/storage_duration
http://en.cppreference.com/w/cpp/language/language_linkage
Register variables
A register variable is a type of local variable.
25
The register keyword tells the compiler “Make accesses to this variable as fast as
possible.”
Increasing the access speed is implementation dependent, but, as the name suggests,
it is often done by placing the variable in a register. There is no guarantee that the
variable will be placed in a register or even that the access speed will increase. It is
a hint to the compiler.
There are restrictions to the use of register variables
You cannot take or compute the address of a register variable.
A register variable can be declared only within a block (you cannot have
global or static register variables).
You can, however, use a register variable as a formal argument in a function
In general, you shouldn’t try to second-guess the compiler’s optimizer, since
it will probably do a better job than you can. Thus, the register keyword is
best avoided.
Static***
Variables defined local to a function disappear at the end of the function scope.
When you call the function again, storage for the variables is created a new and the
values are re-initialized. If you want a value to be extant throughout the life of a
program, you can define a function’s local variable to be static and give it an initial
value.
The initialization is performed only the first time the function is called, and the data
retains its value between function calls. If you do not provide an initializer for a
static variable of a built-in type, the compiler guarantees that variable will be
initialized to zero
The rules are the same for static objects of user-defined types, including the fact
that some initialization is required for the object. If you don’t specify constructor
arguments when you define the static object, the class must have a default
constructor. For example
//StaticObjectsInFunctions.cpp
#include <iostream>
using namespace std;
class X
{
int i;
public:
X(int ii = 0) : i(ii) {} // Default
~X() { cout << "X::~X()" << endl; }
};
void f()
{
static X x1(47);
static X x2; // Default constructor required
}
int main()
{
f();
26
}
The construction of static object of X inside function f() occurs the first time
control passes through the definition, and only the first time.
Unlike global variable a static variable unavailable outside the scope of the
function, so it can’t be inadvertently changed. This localizes errors.
Static object destructors:
Destructors for static objects (that is, all objects with static storage, not just
local static objects) are called when main( ) exits or when the Standard C
library function exit1( )is explicitly called. Static object destructors are not
called if you exit the program using the Standard C library function abort( ).
Like ordinary destruction, destruction of static objects occurs in the reverse
order of initialization. However, only objects that have been constructed are
destroyed.
The C++ development tools keep track of initialization order and the objects
that have been constructed. Global objects are always constructed before
main( ) is entered and destroyed as main( ) exits, but if a function containing
a local static object is never called, the constructor for that object is never
executed, so the destructor is also not executed.
When static is applied to a function name or to a variable that is outside of all
functions, it means “This name is unavailable outside of this file.” The function
name or variable is local to the file; we say it has file scope. As a demonstration,
compiling and linking the following two files will cause a linker error:
//FileStatic.cpp
// File scope demonstration. Compiling and linking this file with FileStatic2.cpp
// will cause a linker error
// File scope means only available in this file:
static int fs;
int main()
{
fs = 1;
}
//FileStatic2.cpp {O}
//Trying to reference fs
extern int fs;
void func()
{
fs = 100;
}
Even though the variable fs is claimed to exist as an extern in the FileStatic2.cpp,
the linker won’t find it because it has been declared static in FileStatic.cpp.
Ordinarily, any name at file scope (that is, not nested inside a class or function) is
visible throughout all translation units in a program. This is often called external
1
In most implementations, main( ) just calls exit( ) when it terminates. This means that it can be
dangerous to call exit( ) inside a destructor because you can end up with infinite recursion.
You can specify actions to take place when leaving main( ) (or calling exit( )) by using the Standard C
library function atexit( ). In this case, the functions registered by atexit( )may be called before the
destructors for any objects constructed before leaving main( ) (or calling exit( ))
27
linkage because at link time the name is visible to the linker everywhere, external to
that translation unit.
Global variables and ordinary functions have external linkage. There are times
when you’d like to limit the visibility of a name. You might like to have a variable
at file scope so all the functions in that file can use it, but you don’t want functions
outside that file to see or access that variable, or to inadvertently cause name
clashes with identifiers outside the file. An object or function name at file scope that
is explicitly declared static is local to its translation unit. That name has internal
linkage. This means that you can use the same name in other translation units
without a name clash.
One advantage to internal linkage is that the name can be placed in a header file
without worrying that there will be a clash at link time. Names that are commonly
placed in header files, such as const definitions and inline functions, default to
internal linkage
Note that linkage refers only to elements that have addresses at link/load time; thus,
class declarations and local variables have no linkage.
All global objects implicitly have static storage class, so if you say (at file scope),
int a = 0;
then storage for a will be in the program’s static data area, and the initialization for
a will occur once, before main( ) is entered. In addition, the visibility of a is global
across all translation units. In terms of visibility, the opposite of static (visible only
in this translation unit) is extern, which explicitly states that the visibility of the
name is across all translation units. So the definition above is equivalent to saying
extern int a = 0;
But if you say instead,
static int a = 0;
All you’ve done is change the visibility, so ‘a’ has internal linkage. The storage
class is unchanged – the object resides in the static data area whether the visibility is
static or extern.
Global variables***
Global variables are defined outside all function bodies and are available to all parts
of the program (even code in other files).
Global variables are unaffected by scopes and are always available (i.e., the lifetime
of a global variable lasts until the program ends).
If the existence of a global variable in one file is declared using the extern keyword
in another file, the data is available for use by the second file.
Global variables are variables declared outside any block including main function.
They are visible in all blocks in all files in the same process.
In files, other than the one where the global variable is defined, you have to use
keyword extern to tell the compiler: "The following global variable whose name is
xxx and whose type is xxx is defined elsewhere, and you have to find out where
yourself."
Global variables can be defined in either a header file or a source file.
But if you define a global variable in a header file, then when more than one files
include that header file, multiple copies of the same global variable will be
28
instantiated, and you will have link errors. So you should normally put the
definition of global variables in a source file.
Examples:
Example1
29
global variables in it. Logically this header file should have the same name as the
source file, but it can be different (as shown in the following example). Then all
files which accesses the global variables can simply include this header file:
Example2
30
123
123
The program may work, and it may not. It completely depends on the order the files
are initialized in the programming environment.
There are three approaches to dealing with this problem:
1. Don’t do it. Avoiding static initialization dependencies is the best solution.
31
2. If you must do it, put the critical static object definitions in a single file, so you
can portably control their initialization by putting them in the correct order.
3. If you’re convinced it’s unavoidable to scatter static objects across translation
units – as in the case of a library, where you can’t control the programmer who
uses it – there are two programmatic techniques (the third one was used by
comiler writers to to solve stream initialization/deinitialization issues) the
problem.
o Construct on First Use Idiom: - The basic idea of the Construct on First
Use Idiom is to wrap your static object inside a function.
For example, suppose you have two classes, Fred and Barney. There is a
namespace-scope / global Fred object called x, and a namespace-scope /
global Barney object called y. Barney’s constructor invokes the
goBowling() method on the x object. The file x.cpp defines the x object:
The Problem
The downside of this approach is that the Fred object is never destructed. If
the Fred object has a destructor, there is another technique (with important
side effects) that answers this concern; but it needs to be used with care
since it creates the possibility of another (equally nasty) problem
o Construct on First Use Idiom without Leak: - sometimes people worry about
the fact that the previous solution “leaks.” In many cases, this is not a
32
problem, but it is a problem in some cases. Note: even though the object
pointed to by ans in the previous FAQ is never deleted, the memory doesn’t
actually “leak” when the program exits since the operating system
automatically reclaims all the memory in a program’s heap when that
program exits. In other words, the only time you’d need to worry about this
is when the destructor for the Fred object performs some important action
(such as writing something to a file) that must occur sometime while the
program is exiting.
In those cases where the construct-on-first-use object (the Fred, in this case)
needs to eventually get destructed, you might consider changing
function x()as follows:
However there is (or rather, may be) a rather subtle problem with this
change
By changing the declaration from static Fred* ans = new Fred(); to static
Fred ans;, we still correctly handle the initialization situation but we no
longer handle the deinitialization situation. For example, if there are 3 static
objects, say a, b and c, that use ans during their destructors, the only way to
avoid a static deinitialization disaster is if ans is destructed after all three.
The point is simple: if there are any other static objects whose destructors
might use ans after ans is destructed, bang, you’re dead. If
the constructors of a, band c use ans, you should normally be okay since the
runtime system will, during static deinitialization, destruct ans after the last
of those three objects is destructed. However if a and/or b and/or c fail to
use ans in their constructors and/or if any code anywhere gets the address
of ans and hands it to some other static object, all bets are off and you have
to be very, very careful.
33
The definition must occur outside the class (no inlining is allowed), and only
one definition is allowed. Thus, it is common to put it in the implementation
file for the class.
With static consts of integral types you can provide the definitions inside the class,
but for everything else (including arrays of integral types, even if they are static
const) you must provide a single external definition for the member. These
definitions have internal linkage, so they can be placed in header files.
You can also create static const objects of class types and arrays of such objects.
However, you cannot initialize them using the “inline syntax” allowed for
staticconsts of integral built-in types:
Example:
//StaticObjectArrays.cpp
// Static arrays of class objects
class X
{
int i;
public:
X(int ii) : i(ii) {}
};
class Stat
{
//static consts are initialized in-place:
static const int scSize = 100;
//Arrays, Non-integral and non-const statics must be initialized externally:
static const int scInts[];
static const long scLongs[];
static int size;
static float table[];
X Stat::x2(100);
X Stat::xTable2[] = { X(1), X(2), X(3), X(4) };
const X Stat::x3(100);
const X Stat::xTable3[] = { X(1), X(2), X(3), X(4) };
int main()
{
34
Stat v;
}
You cannot have static data members inside local classes (a local class is a class
defined inside a function). Thus
//Local class cannot have static data members:
void f()
{
class Local
{
public:
//! static int i; // Error
//(How would you define i?)
} x;
}
This is because static methods don't have an entry in the vtable, and can't thus
be virtual.
Extern***
It tells the compiler that a variable or a function exists, even if the compiler hasn’t
yet seen it in the file currently being compiled.
This variable or function may be defined in another file or further down in the
current file.
Example
//Forward.cpp
// Forward function & data declarations
#include <iostream>
using namespace std;
// This is not actually external, but the compiler must be told it exists somewhere:
extern int i;
extern void func();
int main()
{
i = 0;
func();
}
int i; // The data definition
35
void func()
{
i++;
cout << i;
}
When the compiler encounters the declaration ‘extern int i’, it knows that the
definition for i must exist somewhere as a global variable. When the compiler
reaches the definition of i, no other declaration is visible, so it knows it has found
the same i declared earlier in the file.
If you were to define i as static, you would be telling the compiler that i is defined
globally (via the extern), but it also has file scope (via the static), so the compiler
will generate an error.
Alternate linkage specifications :
If you’re writing a program in C++ and you want to use a C library, the linkage may
not happen correctly.
If you make the C function declaration,
float f(int a, char b);
The C++ compiler will decorate this name to something like _f_int_char to support
function overloading (and type-safe linkage). However, the C compiler that
compiled your C library has most definitely not decorated the name, so its internal
name will be _f. Thus, the linker will not be able to resolve your C++ calls to f( ).
The escape mechanism provided in C++ is the alternate linkage specification, which
was produced in the language by overloading the extern keyword. The extern is
followed by a string that specifies the linkage you want for the declaration,
followed by the declaration:
extern "C" float f(int a, char b);
If you have a group of declarations with alternate linkage, put them inside braces,
like this:
extern "C" {
float f(int a, char b);
double d(int a, char b);
}
extern "C" {
#include "Myheader.h"
}
Most C++ compiler vendors handle the alternate linkage specifications inside their
header files that work with both C and C++, so you don’t have to worry about it.
Linkage***
In an executing program, an identifier is represented by storage in memory that
holds a variable or a compiled function body. Linkage describes this storage as it is
seen by the linker.
There are two types of linkage: internal linkage and external linkage.
36
Internal linkage
o Internal linkage means that storage is created to represent the identifier
only for the file being compiled. Other files may use the same identifier
name with internal linkage, or for a global variable, and no conflicts will be
found by the linker – separate storage is created for each identifier. Internal
linkage is specified by the keyword static in C and C++.
External linkage
o External linkage means that a single piece of storage is created to represent
the identifier for all files being compiled. The storage is created once, and
the linker must resolve all other references to that storage. Global variables
and function names have external linkage. These are accessed from other
files by declaring them with the keyword extern.
o Variables defined outside all functions (with the exception of const in C++)
and function definitions default to external linkage. You can specifically
force them to have internal linkage using the static keyword.
o You can explicitly state that an identifier has external linkage by defining it
with the extern keyword. Defining a variable or function with extern is not
necessary in C, but it is sometimes necessary for const in C++.
Automatic (local) variables exist only temporarily, on the stack, while a function is
being called. The linker doesn’t know about automatic variables, and so these have no
linkage
Constants***
http://en.cppreference.com/w/cpp/language/cv
http://en.cppreference.com/w/cpp/language/constant_expression
Any data type, built-in or user-defined, may be defined as const. If you define
something as const and then attempt to modify it, the compiler will generate an
error.
In C++, a const must always have an initialization value otherwise there is a
compiler error.
Constant values for built-in types are expressed as decimal, octal, hexadecimal, or
floating-point numbers (sadly,binary numbers were not considered important), or as
characters
Example: -
const int PerfectNumber; //Compiler Error; const has to be initialized
const int PerfectNumber = 10; //OK;
extern int PrefectNumber; //No Compiler Error; as it works as a
declaration //and compiler assumes the definition
in some //other translation unit.
//There may be a linker error if PrefectNumber is
not defined in other translation unit.
You can place const definitions inside header files and distribute it to translation
units by including the header file.
By default constant has internal linkage, that is, it is visible only within the file
where it is defined and cannot be seen at link time by other translation units. The
const can be made to have external linkage, if extended explicitly in the definition.
37
Example
extern int PrefectNumber = 10;
Storage allocation: Normally, the C++ compiler avoids creating storage for a
const, but instead holds the definition in its symbol table. In the ordinary case no
storage is allocated. When the const is used, it is simply folded in at compile time
You force the storage allocation for const
o When you use extern with const : In this case storage must be allocated
because extern says “use external linkage,” which means that several
translation units must be able to refer to the item, which requires it to have
storage
o When you take the address of a const
o When you declare a complicated structure as a const: In this case you’re
virtually assured that the compiler will not be sophisticated enough to keep
an aggregate in its symbol table, so storage will be allocated Storage.
Whenever the compiler must allocate storage, constant folding is prevented (since
there’s no way for the compiler to know for sure what the value of that storage is –
if it could know that, it wouldn’t need to allocate the storage).
Compiler might not do a constant folding2 but if folded and no extern specified
there will be no location
It’s possible to use const for aggregates, but you’re virtually assured that the
compiler will not be sophisticated enough to keep an aggregate in its symbol table,
so storage will be allocated.
In these situations, const means “a piece of storage that cannot be changed.”
However, the value cannot be used at compile time because the compiler is not
required to know the contents of the storage at compile time. In the following
code, you can see the statements that are illegal:
//Constag.cpp
// Constants and aggregates
const int i[] = { 1, 2, 3, 4 };
//! float f[i[3]]; // Illegal
2
Constant folding refers to reducing constant expressions at compile time. Nearly all C++ compilers fold
arithmetic expressions such as ``2+2'' (to “4”) at compile time. Programmers generally do not write
expressions such as (3 + 5) directly, but these expressions are relatively common after macro expansion
and other optimizations such as constant propagation.
38
struct S { int i, j; };
const S s[] = { { 1, 2 }, { 3, 4 } };
//! double d[s[1].j]; // Illegal
int main() {}
Since a const in C++ defaults to internal linkage, you can’t just define a const in one
file and reference it as an extern in another file. To give a const external linkage so
it can be referenced from another file, you must explicitly define it as extern, like
this:
extern const int x = 1;
Notice that by giving it an initializer and saying it is extern, you force storage to be
created for the const (although the compiler still has the option of doing constant
folding here). The initialization establishes this as a definition, not a declaration.
The declaration:
extern const int x
39
}
int main()
{
const Y yy;
yy.f(); // Actually changes it!
}
The problem with this approach is that this lack of constness is hidden away in a
member function definition, and you have no clue from the class interface that the data
of the object is actually being modified unless you have access to the source code.
To second approach is to put everything out in the open by using the mutable keyword
in the class declaration to specify that a particular data member may be changed inside
a const object
//Mutable.cpp
//The "mutable" keyword
class Z
{
int i;
mutable int j;
public:
Z();
void f() const;
};
Z::Z() : i(0), j(0) {}
void Z::f() const
{
//! i++; // Error -- const member function
j++; // OK: mutable
}
int main()
{
const Z zz;
zz.f(); // Actually changes it!
}
Volatile
The qualifier const tells the compiler “This never changes” (which allows the
compiler to perform extra optimizations), the qualifier volatile tells the compiler
“You never know when this will change,” and prevents the compiler from
performing any optimizations based on the stability of that variable.
Use this keyword when you read some value outside the control of your code, such
as a register in a piece of communication hardware
A volatile variable is always read whenever its value is required, even if it was just
read the line before
References
Reference has to be initialized.
Dereferenced automatically
Associated with the same variable throughout its life.
40
Reference is never null.
Pointer to Reference can be created
Example: -
int a(10),b(20);
int& ref(a);
int* pRef = &ref;
Reference to a Pointer is also possible and useful in situations in which you want to
pass a pointer to some function which will modify it.
So you can use Reference to pointer instead of pointer to pointer.
Example
void fun(int*& ref) //fun is changing ref so the change will be reflected in
main
Array of pointers is possible but array of References is illegal in C++
deltype specifier***
http://en.cppreference.com/w/cpp/language/decltype
auto specifier***
http://en.cppreference.com/w/cpp/language/auto
http://en.cppreference.com/w/cpp/language/alignas
typedef specifier***
http://en.cppreference.com/w/cpp/language/typedef
Typename*** (???)
The keyword typename was introduced to specify that the identifier that follows is a
type
Example
template<typename T>
struct first {
typedef T * pointer;
};
41
template<typename T>
class second {
first<T>::pointer p; // syntax error
};
Here, typename is used to clarify that SubType is a type of class T. Thus, ptr is a
pointer to the type T::SubType
According to the qualification of SubType being a type, any type that is used in
place of T must provide an inner type SubType.
Note that typename is always necessary to qualify an identifier of a template as
being a type, even if an interpretation that is not a type would make no sense. Thus,
the general rule in C++ is that any identifier of a template is considered to be a
value, except it is qualified by typename
Apart from this, typename can also be used instead of class in a template
declaration: template <typename T> class MyClass
Real Example in STL
???
http://en.cppreference.com/w/cpp/language/elaborated_type_specifier
Static Assertion***
http://en.cppreference.com/w/cpp/language/static_assert
asm declaration***
http://en.cppreference.com/w/cpp/language/asm
42
Definition
Definition is an instruction to the compiler to allocate space.
When and where allocated depends on the storage class.
Definitions are declarations that fully define the entity introduced by the
declaration. Every declaration is a definition, except for the following:
A function declaration without a function body
Any declaration with an extern storage class specifier or with a language
linkage specifier (such as extern "C") without an initializer
A typedef declaration
An alias-declaration
A using-declaration
Declaration of a deduction guide (does not define any entities). See Class
template deduction***
A static_assert declaration (does not define any entities)
An attribute declaration (does not define any entities)
An empty declaration (does not define any entities)
A using-directive (does not define any entities)
43
An explicit instantiation declaration (an "extern template")
44
Initialization***
http://en.cppreference.com/w/cpp/language/initialization
Default initialization***
http://en.cppreference.com/w/cpp/language/default_initialization
Value initialization***
http://en.cppreference.com/w/cpp/language/value_initialization
Copy initialization***
http://en.cppreference.com/w/cpp/language/copy_initialization
Direct initialization***
http://en.cppreference.com/w/cpp/language/direct_initialization
Aggregrate initialization***
http://en.cppreference.com/w/cpp/language/aggregate_initialization
http://en.cppreference.com/w/cpp/language/list_initialization
Reference initialization***
http://en.cppreference.com/w/cpp/language/reference_initialization
45
Static non local initialization***
http://en.cppreference.com/w/cpp/language/initialization#Non-local_variables
Zero initialization***
http://en.cppreference.com/w/cpp/language/initialization#Non-local_variables
Constant initialization***
http://en.cppreference.com/w/cpp/language/constant_initialization
http://en.cppreference.com/w/cpp/language/initialization#Non-local_variables
46
Expressions
Value Categories
Each C++ expression is characterized by two independent properties: a type and a
value category. Each expression has some non-reference type, and each expression
belongs to exactly one of the three primary value categories: prvalue, xvalue,
lvalue, defined as follows:
o a glvalue is an expression whose evaluation determines the identity of an
object, bit-field, or function
o a prvalue is an expression whose evaluation either
computes the value of the operand of an operator (such prvalue has
no result object), or
Initializes an object or a bit-field (such prvalue is said to have a
result object). All class and array prvalues have a result object even
if it is discarded.
o an xvalue is a glvalue that denotes an object or bit-field whose resources can
be reused
o An lvalue is a glvalue that is not an xvalue.
o An rvalue is a prvalue or an xvalue.
Note: this taxonomy went through significant changes with past C++ standard
revisions, see History below for details.
Primary categories
lvalue: -The following expressions are lvalue expressions:
the name of a variable or a function in scope, regardless of type, such
as std::cin or std::endl. Even if the variable's type is rvalue reference, the
expression consisting of its name is an lvalue expression;
a function call or an overloaded operator expression of lvalue reference return
type, such as std::getline(std::cin, str), std::cout << 1, str1 = str2, or ++it;
a = b, a += b, a %= b, and all other built-in assignment and compound
assignment expressions;
++a and --a, the built-in pre-increment and pre-decrement expressions;
*p, the built-in indirection expression;
a[n] and p[n], the built-in subscript expressions, except where a is an array
rvalue (since C++11);
a.m, the member of object expression, except where m is a member enumerator
or a non-static member function, or where a is an rvalue and m is a non-static data
member of non-reference type;
p->m, the built-in member of pointer expression, except where m is a member
enumerator or a non-static member function;
a.*mp, the pointer to member of object expression, where a is an lvalue
and mp is a pointer to data member;
p->*mp, the built-in pointer to member of pointer expression, where mp is a
pointer to data member;
a, b, the built-in comma expression, where b is an lvalue;
a ? b : c, the ternary conditional expression for some a, b, and c;
a string literal, such as "Hello, world!";
a cast expression to lvalue reference type, such as static_cast<int&>(x);
47
a function call or an overloaded operator expression of rvalue
reference to function return type;
(since C++11)
a cast expression to rvalue reference to function type, such
as static_cast<void (&&)(int)>(x).
Properties:
Same as glvalue (below).
Address of an lvalue may be taken: &++i [1] and &std::endl are valid
expressions.
A modifiable lvalue may be used as the left-hand operand of the built-in
assignment and compound assignment operators.
An lvalue may be used to initialize an lvalue reference; this associates a new
name with the object identified by the expression.
Expression parser
Deciding operator does not depend on precedence or association; it works as follows:
Compiler is greedy :- first phase is lexical analyzer which is greedy and finds the
longest possible token
For Example: -
int a = 10, b = 5;
a --- b;
//a - (--b) No
// a -- (-b) OK because of compiler’s greediness.
Operators
Logical operators***
http://en.cppreference.com/w/cpp/language/operator_logical
Bitwise operators***
The bitwise operators
Operator Name Description
a&b and 1 if both bits are 1. 3 & 5 is 1.
a|b or 1 if either bit is 1. 3 | 5 is 7.
a^b xor 1 if both bits are different. 3 ^ 5 is 6.
48
This unary operator inverts the bits. If ints are stored as 32-bit
~a not
integers, ~3 is 11111111111111111111111111111100.
left Shifts the bits of n left p positions. Zero bits are shifted into the
n<<p
shift low-order positions. 3 << 2 is 12.
Shifts the bits of n right p positions. If n is a 2's complement
right
n>>p signed number, the sign bit is shifted into the high-order positions.
shift
5 >> 2 is 1.
Shift operators
Usage
I << n (left shift I by n bits)
I >> n (Right shift I by n bits)
The left-shift operator (<<) produces the operand to the left of the operator shifted
to the left by the number of bits (n) specified after the operator.
The right-shift operator (>>) produces the operand to the left of the operator
shifted to the right by the number of bits (n) specified after the operator.
If the value (n) after the shift operator is greater than the number of bits in the left-
hand operand, the result is undefined.
If the left-hand operand is unsigned, the right shift is a logical shift so the upper bits
will be filled with zeros. If the left-hand operand is signed, the right shift may or
may not be a logical shift (that is, the behavior is undefined).
Shifts can be combined with the equal sign (<<= and >>=). The lvalue is replaced
by the lvalue shifted by the rvalue.
The << and >> provide bit-shifting behavior, but when they shift bits off the end of
the number, those bits are lost. When manipulating bits you can also perform
rotation, which means that the bits that fall off one end are inserted back at the other
end, as if they’re being rotated around a loop.
Rotation Examples:
//Rotation.cpp
// Perform left and right rotations
unsigned char rol(unsigned char val)
{
int highbit;
if(val & 0x80) // 0x80 is the high bit only
highbit = 1;
else
highbit = 0;
//Left shift (bottom bit becomes 0):
val <<= 1;
//Rotate the high bit onto the bottom:
val |= highbit;
return val;
}
unsigned char ror(unsigned char val)
{
49
int lowbit;
if(val & 1) //Check the low bit
lowbit = 1;
else
lowbit = 0;
val >>= 1; //Right shift by one position
//Rotate the low bit onto the top:
val |= (lowbit << 7);
return val;
}
50
Shift right divides by 2
OR
Note that shifting by zero places is a legal operation -- we'll just get back the same
number we started with
To clear a specified bit: - To clear the state of a bit (e.g. pos_num) we need a
number for which the pos_num is 0 and all other bits are 1. This can be achieved by
shifting 1 pos_num bits to the left and then complement the result.
To flip a specified bit: - To check the state of a bit (e.g. pos_num) in a number (e.g.
in_use) basically we need a number for which the pos_num bit is 1 and all other bits
are 0.
When we perform the XOR with this number the state of the specified bit is
flipped.
Write a generic method to convert any unsigned number (int or long) to binary
number ???
Example
a = --b ? b : (b = -99);
--b ? b : (b = -99);
Here the second b is superfluous, since the value produced by the operator is
unused. An expression is required between the ? and :. In this case, the expression
could simply be a constant that might make the code run a bit faster i.e. it can be
rewritten as
const int X;
--b ? X : (b = -99);
52
a = b, c;
is equivalent to:
(a = b), c;
Another interesting fact is that the comma operator introduces a sequence point.
This means that the expression:
a+b, c(), d
is guaranteed to have its three subexpressions (a+b, c() and d) evaluated in order.
This is significant if they have side-effects. Normally compilers are allowed to
evaluate subexpressions in whatever order they find fit; for example, in a function
call:
someFunc(arg1, arg2, arg3)
Take care to notice that the comma operator may be overloaded in C++. The actual
behavior may thus be very different from the one expected.
Common operators
53
1 :: Scope resolution Left-to-right
() Function call
2
[] Array subscripting
3 * Indirection (dereference)
& Address-of
sizeof Size-of[note 1]
54
4 .* ->* Pointer to member Left-to-right
14 || Logical OR
55
Assignment by product, quotient, and
*= /= %=
remainder
17 , Comma Left-to-right
Conversions
Casting operators
C-style cast
C style cast are casts using (type)object or type(object).
Generally the C-style cast will employ whatever combination of the below listed
casts is necessary to achieve the specified type conversion i.e. it is defined as the
first of the following which succeeds:
const_cast
static_cast
static_cast then const_cast
reinterpret_cast
reinterpret_cast then const_cast
It can therefore be used as a replacement for other casts in some instances, but can
be extremely dangerous because of the ability to devolve into a reinterpret_cast, and
the latter should be preferred when explicit casting is needed, unless you are sure
static_cast will succeed or reinterpret_cast will fail.
C-style casts also ignore access control when performing a static_cast, which means
that they have the ability to perform an operation that no other cast can. This is
mostly a kludge and is just another reason to avoid C-style casts.
The new casts are less powerful and more specific than old-style cast.
Each of the new cast has separate purposes, thus give the program more precise
control.
56
Old style casting is still legal, but new casts are preferable
static_cast
It is mainly used to cast up or down the inheritance hierarchy.
It is used for cases where you basically want to reverse an implicit conversion, with
a few restrictions and additions.
It only performs type checking at compile time. So it is safer than casting with "( )".
It performs no runtime checks so it is the programmer's responsibility to make
sure that pBase is pointing to a Derived object. If not, there will be a run time
error. Thus this should be used if you know that you refer to an object of a specific
type, and thus a check would be unnecessary
It doesn't do any run time checking of the types involved, which means that unless
you know what you are doing, they could be very unsafe.
It also only allows casting between related types, such as pointers or references
between Base and Derived, or between fundamental types, such as long to int or int
to float.
It does not allow casts between fundamentally different types, such as a cast
between a BaseA and BaseB if they are not related. This will result in a compile
time error.
It can also cast through inheritance hierarchies. It is unnecessary when casting
upwards (towards a base class), but when casting downwards it can be used as long
as it doesn't cast through virtual inheritance
Example:
57
dynamic_cast
The dynamic_cast can only be used with pointers and references to objects.
It makes sure that the result of the type conversion is valid and complete object of
the request class.
dynamic_cast does run time checking as well, and if the instance cannot be cast into
another derived type, it will return a null pointer.
This cast is used for cases where you don't know what the dynamic type of the
object is.
You cannot use dynamic_cast if you downcast and the argument type is not
polymorphic.
With dynamic_cast it is also possible to cast null pointers even between the pointers
of unrelated classes. Dynamic_cast can cast pointers of any type to void pointer
(void*)
dynamic_cast returns a null pointer if the cast is not successful. (When you cast to a
reference, a bad_cast exception is thrown in that case).
The following code is not valid, because Base is not polymorphic (doesn't contain a
virtual function):
58
ii. It also can only go through public inheritance - it will always fail to travel
through protected or private inheritance. This is rarely an issue; however, as
such forms of inheritance are rare.
Example:
An "up-cast" is always valid with both static_cast and dynamic_cast, and also
without any cast, as an "up-cast" is an implicit conversion
const_cast
const_cast casts away const or volatile. It can be used to remove or add const to a
variable; no other C++ cast is capable of removing it (not even reinterpret_cast)
It only works on pointers, not objects.
You can use it to modify a data member in a const method:
It is important to note that using it is only undefined if the original variable is
const; if you use it to take the const of a reference to something that wasn't
declared with const, it is safe.
This can be useful when overloading member functions based on const, for
instance. It can also be used to add const to an object, such as to call a member
function overload.
const_cast also works similarly on volatile, though that's less common
reinterpret_cast
reinterpret_cast is the most dangerous cast, and should be used very sparingly.
It turns one type directly into another - such as casting the value from one pointer to
another, or storing a pointer in an int, or all sorts of other nasty things
Largely, the only guarantee you get with reinterpret_cast is that if you cast the result
back to the original type, you will get the same value
reinterpret_cast is used for cases that yield implementation-dependent results,
such as casting between function pointer types.
59
Because it allows you to do nonstandard strange conversions, dangerous
manipulations can be done, and the result may be machine-dependent.
typeid()
The C++ standard provides the typeid() operator for getting type information.
The typeid() operator can be used with :
Variables
Expressions
Data-types
The typeid returns a const reference of class type_info, which is a class description
of the operand
Class type_info has a method name(), which returns a char * which is the type
name of the operand
Example
60
If class Base and Derived are polymorphic types i.e. they have virtual
functions, the output will be:
typeid(pBase1) = class Base1 *
typeid(*pBase1) = class Derived
If class Base and Derived do not have virtual functions, the output will be:
typeid(pBase1) = class Base1 *
typeid(*pBase1) = class Base
A bad_typeid exception is thrown by typeid(), if the type that is evaluated by
typeid is a null pointer and is preceded by a dereference operator.
type_info
The type_info class has only a few members functions:
const char* name() for getting the string representation of the type. For
example ‘int’ or ‘MyClass’ etc.
bool before(const type_info&) for ordering
operator==() and operator! =() for comparing type_info objects.
standard conversions***
http://en.cppreference.com/w/cpp/language/implicit_conversion
const_cast conversion
http://en.cppreference.com/w/cpp/language/const_cast
static_cast conversion
http://en.cppreference.com/w/cpp/language/static_cast
61
dynamic_cast conversion
http://en.cppreference.com/w/cpp/language/dynamic_cast
reinterpret_cast conversion
http://en.cppreference.com/w/cpp/language/reinterpret_cast
User-defined conversion
http://en.cppreference.com/w/cpp/language/cast_operator
Null-Terminated Strings***
C++ provides following two types of string representations:
The C-style character string
The string class type
The C-style character (null-terminated) string is actually a one-dimensional array of
characters which is terminated by a null character '\0'.
Initialization of null-terminated character sequences
The C++ compiler automatically places the '\0' at the end of the string when
it initializes the array
Because string literals are regular arrays, they have the same restrictions as
these, and cannot be assigned values.
Once myword has already been declared, the below expressions would not
be valid:
myword = "Bye"; \\Invalid; Assignment not possible
myword[] = "Bye"; \\Invalid; Assignment not possible
myword = { 'B', 'y', 'e', '\0' }; \\Invalid; Assignment not possible
The above expressions are invalid because arrays cannot be assigned values.
Note, though, that each of its elements can be assigned a value individually.
62
For example, this would be correct:
myword[0] = 'B';
myword[1] = 'y';
myword[2] = 'e';
myword[3] = '\0';
63
Classes
Encapsulation
Put together attributes and behavior.
Hide implementation.
Expose interface.
Server code modified should not cascade into changes in the client code.
Abstraction
Selecting essential features.
Ignore what is not essential.
Class types
-----------------------------------------------
No member can be declared as auto, extern, or register
Methods provided by compiler by default
i. default constructor
ii. destructor
iii. copy constructor
iv. assignment operator
v.
Like C++ class both struct and union can also have a constructor, destructor, member
functions, and even access control. The only difference between struct and class in C++
is that struct defaults to public and class defaults to private.
However, a union cannot be used as a base class during inheritance, which is quite
limiting from an object-oriented design standpoint. The reason for using a union as a
class is to save space. Instead of replacing class with union, the correct design to
achieve this is by encapsulating union in a class.
Example
//SuperVar.cpp
//A super-variable
#include <iostream>
using namespace std;
class SuperVar
{
enum
{
character,
integer,
floating_point
} vartype; // Define one
union
{ // Anonymous union
char c;
int i;
float f;
64
};
public:
SuperVar(char ch);
SuperVar(int ii);
SuperVar(float ff);
void print();
};
SuperVar::SuperVar(char ch)
{
vartype = character;
c = ch;
}
SuperVar::SuperVar(int ii)
{
vartype = integer;
i = ii;
}
SuperVar::SuperVar(float ff)
{
vartype = floating_point;
f = ff;
}
void SuperVar::print()
{
switch (vartype)
{
case character:
cout << "character: " << c << endl;
break;
case integer:
cout << "integer: " << i << endl;
break;
case floating_point:
cout << "float: " << f << endl;
break;
}
}
int main()
{
SuperVar A('c'), B(12), C(1.44F);
A.print();
B.print();
C.print();
}
65
o Variable declaration
o Function declaration
o Constant definition (should not be extern)
o type definitions
o inline fn definition
o Template function definition (why?)
Union types
If a union has no type name and no variable name, it’s called an anonymous union,
and creates space for the union but doesn’t require accessing the union elements
with a variable name and the dot operator. For instance, if your anonymous union
is:
//AnonymousUnion.cpp
int main()
{
union
{
int i;
float f;
};
//Access members without using qualifiers:
i = 12;
f = 1.22;
}
Note that you access members of an anonymous union just as if they were ordinary
variables. The only difference is that both variables occupy the same space.
If the anonymous union is at file scope (outside all functions and classes) then it
must be declared static so it has internal linkage.
Constructors
Default constructor
Default constructor is called implicitly when you create an array of objects. If you want
to have an array of objects that do not have a default constructor, the work-around is to
have an array of pointers and initialize them using operator new.
One argument constructors
One-argument constructors are called user-defined converters.
Suppose a method has an argument of type "Child", which has an one-argument
constructor "Child (Parent)". When you call this method, if you pass a "Parent"
object instead of "Child", the compiler will implicitly call the one-argument
constructor and convert the "Parent" object to "Child".
Example
66
Explicit constructor***
By using the keyword explicit, you can prohibit a single argument constructor from
defining an automatic type conversion.
A typical example of the need for this feature is in a collection class in which you
can pass the initial size as constructor argument. For example, you could declare a
constructor that has an argument for the initial size of a stack:
Here, the use of explicit is rather important. Without explicit this constructor would
define an automatic type conversion from int to Stack. If this happens, you could
assign an int to a Stack:
The automatic type conversion would convert the 40 to a stack with 40 elements
and then assign it to s. This is probably not what was intended. By declaring the int
constructor as explicit, such an assignment results in an error at compile time.
67
Note that explicit also rules out the initialization with type conversion by using
the assignment
The former creates a new object of type Y by using an explicit conversion from
type X, whereas the latter creates a new object of type Y by using an implicit
conversion.
Constructor gotcha***
Consider that we have a class Ex
Ex ex; //OK; default constructor will be called
68
class X
{
public:
X();
};
X::X() {}
void f(int i)
{
switch(i)
{
case 1 :
X x2; // Error: case bypasses init (X * x2 = new X; also fails)
break;
case 2 : // Error: case bypasses init
X x3;
break;
}
}
int main()
{
f(9);
f(11);
}
Initialization lists***
http://en.cppreference.com/w/cpp/language/initializer_list#Initialization_order
69
{
}
catch ( ... )
{
std::cerr << "Couldn't create _str";
//now, the exception is rethrown as if we'd //written
// "throw;" here
}
private:
string _str;
};
Destructors
Copy Constructor???
The…
According to the C++ standard, the copy constructor for MyClass must have
one of the following signatures:
Note that none of the following constructors, despite the fact that they could do the
same thing as a copy constructor, are copy constructors:
70
If no user-defined copy assignment operators are provided for a class type, the
compiler will always declare one as an inline public member of the class.
This implicitly-declared copy assignment operator has this form, if all of the
following is true.
T& T::operator=(T&).
An assignment operator performs below actions :-
Due to the risk of failure in the memory allocation process for the target object it’s
risky to do the de-allocation so copy-and-swap idiom should be used
A class can have multiple copy assignment operators, e.g.
both T& T::operator=(const T&) and T& T::operator=(T). If some user-defined
copy assignment operators are present, the user may still force the generation of the
implicitly declared copy assignment operator with the keyword default. (since C+
+11)
The implicitly-defined copy assignment operator for a non-union class X performs
memberwise copy assignment of its subobjects. The direct base classes of X are
assigned first, in the order of their declaration in the base-specifier-list, and then the
immediate non-static data members of X are assigned, in the order in which they
were declared in the class definition. Each subobject is assigned in the manner
appropriate to its type:
o If the subobject is of class type, the copy assignment operator for the class is
used (as if by explicit qualification; that is, ignoring any possible virtual
overriding functions in more derived classes);
71
o If the subobject is an array, each element is assigned, in the manner
appropriate to the element type;
o If the subobject is of scalar type, the built-in assignment operator is used.
Because the copy assignment operator is always declared for any class, the base
class assignment operator is always hidden. If a using-declaration is used to bring in
the assignment operator from the base class, and its argument type could be the
same as the argument type of the implicit assignment operator of the derived class,
the using-declaration is also hidden by the implicit declaration.
A C++ 'reference' can only be initialized, not assigned. Therefore, an
object containing a reference can only be initialized, too. So indeed: if you need
assignment on a class, that class cannot have reference member variables.
A static member is shared by all the objects of a class. Therefore, static member’s
assignment must be avoided while implementing the assignment operator for the
class.
If you mark a member variable as const, this expresses that this variable will not
(should not!) change its value during the lifetime of the object. So clearly, assigning
a new value to the object violates this statement. So if you indeed don't want the
member to change, just don't make it const.
An example of assignment operator with const member
72
Transactionization failure
The problem here is that if you’re trying to transactionize the assignment, so that
either all of it happens or none of it happens, this breaks that. If an exception occurs
trying to new up bar1 or bar2, the TFoo part of the object won’t have changed, but
the TSuperFoo part will have. The call to TSuperFoo::operator=() can’t go at the
top of the function.
73
Unfortunately, that’s wrong too. The problem is we’re still hosed if TSuperFoo’s
assignment operator can also throw an exception, which is a reasonable thing to
expect. If we succeed in creating our TBarobjects, but TSuperFoo::operator=() fails
to create whatever he needs to (presumably he’s also transactionized), the object
will correctly be left untouched, but we’ll leak the new TBars we created. So
theright answer (he said sheepishly) is this:
74
The call to TSuperFoo::operator=() has to go inside the try. Notice that it
goes after we create the new TBars. We want to make sure creating the TBars has
succeeded before we
call TSuperFoo::operator=()because TSuperFoo::operator=() might succeed,
changing the object, and we only want to change the object if we can carry out the
whole assignment operation.
One interesting consequence of this is that you can imagine a class with a fairly
deep inheritance chain where every class up the chain has other objects it owns.
You’d call an assignment operator low in the chain, it’d create the objects it needs,
then it’d call its parent, which would create the objects it needs and call its parent,
and so on up the chain. Eventually, all of the new objects would have been created
and would be pointed to by temporary variables on the stack. Then, at the root level,
the assignment would finally begin to be carried out, with objects being deleted and
the object’s data members being changed as each function returned. So the
allocations happen in one order and the assignments and deletions happen in reverse
order, which feels kind of awkward at first glance, but it gets the job done. It also
means that there has to be enough free memory to hold two instances of every
subobject, but there really isn’t a safe way around allocating all the extra memory.
The solution
75
Copy assignment operator can be expressed in terms of copy constructor, destructor,
and the swap() member function, if one is provided
Any assignment operator, even the copy assignment operator, can be virtual. [Note:
for a derived class D with a base class B for which a virtual copy assignment has
76
been declared, the copy assignment operator in D does not override B’s virtual copy
assignment operator.
Example:
77
setX will work perfectly fine if X is a monomorphic class but for our example
where X is polymorphic setX will not work correctly
If either x or newX points to an object of class Y or Z, we’ll slice. Only the
members defined in X will get copied.
If x is an instance of Y or Z, the members defined by Y or Z won’t get led in with
new values.
If newX is an instance of Y or Z, the members defined by Y or Z won’t get copied
into x.
The problem here, of course, is that we’re calling X’s assignment operator even
when x isn’t an instance of X. The obvious solution, therefore, would be to
make X’s assignment operator virtual. Then the correct assignment operator would
be called. If we do this, the assignment operators would look like this:
Now, if x and newX are actually both instances of Y, Y’s assignment operator will
get called and everybody will work right
78
clone()3 is the polymorphic copy constructor.
Another alternative is that setX() doesn’t handle this condition, but some other class
up the inheritance chain will have to, probably by doing the same thing we’re doing
here: deleting the old X and creating a new one of the right class. (There might be
other meaningful ways of handling the exception, but they’d be more application-
specific.)
The other possibility is that nobody handles the exception. We could just declare
"assignment operators shall always be called with like classes on either side of the
equal sign" as a program invariant. In other words, we declare heterogeneous
assignment to be a condition which Should Never Happen.
So then instead of the dynamic cast, you can do a static cast and precede it
with an assert:
But let’s go back to the previous answer for a minute and assume we’re going to
catch the exception and finagle the assignment in setX(). To refresh our
memory, setX() now looks like this:
3
If you need polymorphic copy on a group of related classes, you define a virtual function
called clone() and every class in the tree overrides it to call his own copy constructor. You can’t just
call X’s copy constructor for the same reason you can’t just call X’s assignment operator
79
Now, if x and newX are both instances of X or both instances of Y, we’re cool.
If x is an instance of Y and newX is an instance of X, we’re also cool.
Y::operator=() with throw a bad_cast exception, and we’ll catch it, delete x, and
new up a fresh new Y to assign to x.
What we’d have to do to avoid this is manually check for like class in each
assignment operator and throw the bad_cast ourselves, rather than relying
on dynamic_cast to do it for us.
Instead, my original solution to this problem was to avoid using the assignment
operator in the first place:
I still like this. It’s simple and clear, and it works correctly with no extra hassles
even when x and newX are instances of different classes. The other solution, with
the try/catch blocks, has an advantage in situations where the cost of deleting and
newing the destination object is large and relatively rare (the try costs nothing in
most modern compilers, so you in effect fast-path the case of like classes, but an
actual throw can be quite expensive, so you achieve this fast-path effect at the
expense of the different-classes case).
If the fast-path option makes sense for your application, I’d suggest avoiding the
exception and doing it yourself like this:
80
Now if you avoid using the assignment operator in situations where slicing may be
a problem, we’re still left with the question of whether it makes more sense to make
the assignment operator virtual or non-virtual. I’m tending now to come down on
the side of making the assignment operator virtual with an assert to check for the
different-classes condition (since there’s no way to handle that in the assignment
operator itself and therefore the calling function already has to be aware of the
possibility of polymorphism and handle it).
And actually, this won’t even work because C++’s overload resolution rules will
cause the suppressed version to win in some types of call. For instance, consider a
class like this:
81
Since both someY and someOtherY are instances of Y, the overload resolution rules
will declare the nonvirtual version of operator=() to be the "winner," instead of the
inherited virtual operator=(). Since the nonvirtual operator=() is private, you’ll get
an access-violation error at compile time.
Instead, you’d have to define the default assignment operator to call the virtual one.
In every class that inherits the virtual one. Of course, this means defining it non-
virtual. To see why, imagine if Y in the above example had a subclass called Z.
If Y’s operator=() was virtual, Z would have to override it, then it would have to
override X’s operator=(), and then it would have to replace its own default
assignment operator. Cutting any corners here risks creating situations where the
"winning" function, according to the overload-resolution rules, is a function that is
not accessible or isn’t implemented. Clearly, this gets ridiculous quickly as the
inheritance hierarchy gets deeper.
One side effect in either case is that you have to define an override of the
virtual operator=() even when you don’t strictly need one; otherwise, the "default"
one will hide the virtual one.
Summary:
1. In case of polymorphic class, we should define virtual assignment operator
if we need one.
2. Should we have virtual X& Y::operator=(const X& that); and/or virtual Y&
Y::operator=(const Y& that);
???
3. To avoid, heterogeneous-assignment problem we should use program
invariants.
Heterogeneous-assignments includes
X=Y (slicing)
X=Z (slicing)
Y=X (partial assignment)
Y=Z (dynamic_cast failure)
Z=X (partial assignment) and
Z=Y (dynamic_cast failure)
Example
???
82
Copy Ctor vs Assignment operator
The copy constructor and assignment operator do similar things. They both copy
state from one object to another, leaving them with equivalent semantic state.
This doesn't necessarily mean that the objects are identical: some purely internal
data members (such as caches) might not be copied, or data members pointing to
other objects might end up pointing to different objects that are themselves
semantically equivalent, rather than pointing to the same objects.
The difference between the copy constructor and assignment operator is that the
copy constructor is a constructor — a function whose job it is to turn raw storage
into an object of a specific class. An assignment operator, on the other hand, copies
state between two existing objects i.e. an assignment operator has to take into
account the current state of the object when copying the other object's state into it
Person::~Person()
{
delete[] name;
}
//1
Person::Person()
: age(0), name(NULL)
{
}
//2
Person::Person() : age(0), name(new char)
{
name[0] = '\0';
}
//3
Person::Person(): age(0), name(new char[1])
{
name[0] = '\0';
}
//1 Problem with the first case is if user creates and object and tries to call display,
the program may crash.
Example: -
Person b;
b.disp(); //b.disp() will fails because name is NULL;
84
//2 In the Second case we are allocating memory for name in two different ways. In
default constructor we are considering name as a char pointer and in the other
constructor we are considering it as a pointer to an array of characters.
This creates a need for two destructors which is not possible.
//3. This is the ideal constructor for the class and does not require a specialize
destructor.
Friends
Friend functions
Why friend functions
In order to allow an external function to have access to the private and protected
members of a class we have to declare the prototype of the external function that
will gain access preceded by the keyword friend within the class declaration that
shares its members.
For Example:-
???
85
Obj.Sq(); //Sq() is a member of MyClass
But if you want it to be traditional
Sq(Obj); //Sq() is a friend of MyClass
class List
{
private:
Node *head;
public:
List() : head(0) { }
86
void make(int);
void disp() const;
//requires assignment, copy ctor & dtor ...
};
#endif
//List.cpp
#include "List.h"
void List::make(int n)
{
//create a list of n nodes
Node *t;
int val;
while(n--)
{
cin >>val;
t = new Node(val);
//put it in the list
t->link = head;
head = t;
}
}
void List::disp() const
{
Node *t = head;
while(t)
{
cout << t->val << endl;
t = t->link;
}
}
//Client Code
int main()
{
List l;
l.make(4);
l.disp();
}
87
public:
int val;
Node* link;
Node(int val) : val(val), link(0) { }
};
private:
Node *head;
public:
List() : head(0) { }
void make(int);
void disp() const;
//requires assignment, copy ctor & dtor ...
};
#endif
//Discussion:
1. Client should not know about the Node class as we may change our mind later
on to use something else instead of Node. So Node class has everything as
private and List is made friend of Node so that it can access Node’s members.
2. It is clear that friend function is an interface concept
3. Friend class is an implementation concept which hides a class. For example
Node is hidden class.
4. Another way of hiding a class is to use nested classes
5. If the implementation class (Node) is not a nested class, it should have all its
members as private so that client code does not know about it.
In this case the user class (List) will be the friend of implementation class so
that it can use it.
6. If the class is nested then client code cannot create its instances so there is no
need to make members private and the other class as a friend of it.
Be careful when using friend functions and classes, because it allows the friend
function or class to violate encapsulation. If the details of the class change, the
details of the friend will also be forced to change.
Reducing recompilation***
Read Large Scale C++ Software Design
???
88
Functions ***
89
}
For built-in types, it doesn’t matter whether you return by value as a const, so you
should avoid confusing the client programmer and leave off the const when
returning a built-in type by value.
Returning by value as a const becomes important when you’re dealing with user-
defined types. If a function returns a class object by value as a const, the return
value of that function cannot be an lvalue (that is, it cannot be assigned to or
otherwise modified). For example:
//: C08:ConstReturnValues.cpp
// Constant return by value
//Result cannot be used as an lvalue
class X
{
int i;
public:
X(int ii = 0);
void modify();
};
X::X(int ii) { i = ii; }
void X::modify() { i++; }
X f5()
{
return X();
}
const X f6()
{
return X();
}
void f7(X& x)
{ // Pass by non-const reference
x.modify();
}
int main()
{
f5() = X(1); // OK -- non-const return value
f5().modify(); // OK
// Causes compile-time errors:
//! f7(f5());
//! f6() = X(1);
//! f6().modify();
//! f7(f6());
}
Sometimes, during the evaluation of an expression, the compiler must create
temporary objects and they’re automatically const
In the above example, f5( ) returns a non-constX object. But in the expression:
f7(f5()); the compiler must manufacture a temporary object to hold the return value
of f5( ) so it can be passed to f7( ). This would be fine if f7( ) took its argument by
90
value; then the temporary would be copied into f7( ) and it wouldn’t matter what
happened to the temporary X. However, f7( ) takes its argument by reference, which
means in this example takes the address of the temporary X. Since f7( ) doesn’t take
its argument by const reference, it has permission to modify the temporary object.
But the compiler knows that the
temporary will vanish as soon as the expression evaluation is complete, and thus
any modifications you make to the temporary X will be lost. By making all
temporary objects automatically const, this situation causes a compile-time error so
you don’t get caught by what would be a very difficult bug to find.
Thus, it’s important to use const when returning an object by value if you want
to prevent its use as an lvalue.
If you are passing and returning addresses, const is a promise that the
destination of the address will not be changed
Example
//ConstPointer.cpp
//Constant pointer arg/return
void t(int*) {}
void u(const int* cip)
{
//! *cip = 2; // Illegal -- modifies value
int i = *cip; // OK -- copies value
//! int* ip2 = cip; // Illegal: non-const
}
const char* v()
{
// Returns address of static character array:
return "result of function v()";
}
const int* const w()
{
static int i;
return &i;
}
int main()
{
int x = 0;
int* ip = &x;
const int* cip = &x;
t(ip); // OK
//! t(cip); // Not OK
u(ip); // OK
u(cip); // Also OK
//! char* cp = v(); // Not OK
const char* ccp = v(); // OK
//! int* ip2 = w(); // Not OK
const int* const ccip = w(); // OK
const int* cip2 = w(); // OK
91
//! *w() = 1; // Not OK
}
Default values
Client can introduce default values.
Default arguments to a function can be different in different scopes.
Example: -
//file1.h
int sum(int, int, int = 0);
//file1.cpp
int sum(int x , int y, int z)
{
return x + y + z;
}
//main.cpp
int main()
{
int sum(int, int, int = 10);
int a(10), b(20);
cout << sum(a, b) << endl;
}
Here call to sum is converted to sum(10,20,10) instead of sum(10,20,0);Because in the
main scope default argument is 10.
Defaults can be built over number of stat
Example
//file1.h
int sum(int, int, int = 0);
//file1.cpp
int sum(int x , int y, int z)
{
return x + y + z;
}
//main.cpp
int main()
{
int sum(int, int, int = 10);
int sum(int, int = 20 int);
int a(10), b(20);
cout << sum(111) << endl;
}
Here sum(111) is valid statement and converted to sum(111,20,10);
Only one scope is used at a time
Example: -
//file1.h
92
int sum(int, int, int = 0);
//file1.cpp
int sum(int x , int y, int z)
{
return x + y + z;
}
//main.cpp
int main()
{
int sum(int, int = 20 int);
int a(10), b(20);
cout << sum(111) << endl;
}
Here sum(111) will not compile as in the current scope (i.e. scope of main) sum has
only one default parameter;
Function prototypes
Match arguments to parameters (#, order, type of arguments should match those of
parameters; if possible will do conversions)
Overloading
Holding default values
Function calls***
Order of evaluation of arguments: - All arguments are evaluated before the function
is called but the order is not defined.
Calling convention( __stdcall)
i. This is to indicate the calling convention which is used to call Win32 API
functions.
ii. The callee cleans the stack, so the compiler makes var arg functions __cdecl.
iii. Functions that use this calling convention require a function prototype.
iv. The following list shows the implementation of this calling convention:
Argument-passing order Right to left
Argument-passing By value, unless a pointer or reference
convention type is passed.
Stack-maintenance Called function pops its own arguments
responsibility from the stack.
Name-decoration convention An underscore (_) is prefixed to the name.
The name is followed by the “at” sign (@)
followed by the number of bytes (in
decimal) in the argument list. Therefore,
the function declared as int func( int a,
double b ) is decorated as follows:
_func@12
Function pointers
Once a function is compiled and loaded into the computer to be executed, it
occupies a chunk of memory. That memory, and thus the function, has an address.
A function name is actually the starting address of the function code.
A function pointer holds the address of a function, just like a float pointer holds the
address of a float variable.
A function pointer can point to either a global function or a class function.
Number 1 says “fp1 is a pointer to a function that takes an integer argument and
returns a pointer to an array of 10 void pointers.”
94
Number 2 says “fp2 is a pointer to a function that takes three arguments (int, int,
and float) and returns a pointer to a function that takes an integer argument and
returns a float.”
Number 3 says “fp3 is a pointer to a function that takes no arguments and returns a
pointer to an array of 10 pointers to functions that take no arguments and return
doubles.”
Number 4 is a function declaration instead of a variable definition. It says “f4 is a
function that returns a pointer to an array of 10 pointers to functions that return
integers.”
95
Class method pointer Example
96
Why use pointers to functions*** (???)
Functions as Arguments to Other Functions: If you were to write a sort routine,
you might want to allow the function's caller to choose the order in which the data
is sorted. One way to let your user specify what to do is to provide a flag as an
argument to the function, but this is inflexible; the sort function allows only a fixed
set of comparison types (e.g., ascending and descending).
A much nicer way of allowing the user to choose how to sort the data is simply to
let the user pass in a function to the sort function. This function might take two
pieces of data and perform a comparison on them. We'll look at the syntax for this
in a bit.
Example:
#include <iostream>
#include <algorithm> // for swap
// Note our user-defined comparison is the third parameter
void SelectionSort(int *anArray, int nSize, bool (*pComparison)(int, int))
{
using namespace std;
for (int nStartIndex= 0; nStartIndex < nSize; nStartIndex++)
{
int nBestIndex = nStartIndex;
// Search through every element starting at nStartIndex+1
for (int nCurrentIndex = nStartIndex + 1; nCurrentIndex < nSize;
nCurrentIndex++)
{
// Note that we are using the user-defined comparison here
if (pComparison(anArray[nCurrentIndex], anArray[nBestIndex])) //
COMPARISON DONE HERE
nBestIndex = nCurrentIndex;
}
// Swap our start element with our best element
swap(anArray[nStartIndex], anArray[nBestIndex]);
}
}
// Here is a comparison function that sorts in ascending order
// (Note: it's exactly the same as the previous Ascending() function)
bool Ascending(int nX, int nY)
{
return nY > nX;
}
// Here is a comparison function that sorts in descending order
bool Descending(int nX, int nY)
{
return nY < nX;
}
bool EvensFirst(int nX, int nY)
{
// if nX is not even and nY is, nY goes first
if ((nX % 2) && !(nY % 2))
97
return false;
// if nX is even and nY is not, nX goes first
if (!(nX % 2) && (nY % 2))
return true;
// otherwise sort in Ascending order
return Ascending(nX, nY);
}
// This function prints out the values in the array
void PrintArray(int *pArray, int nSize)
{
using namespace std;
for (int iii=0; iii < nSize; iii++)
cout << pArray[iii] << " ";
cout << endl;
}
int main()
{
int anArray[9] = { 3, 7, 9, 5, 6, 1, 8, 2, 4 };
// Sort the array in descending order using the Descending() function
SelectionSort(anArray, 9, Descending);
PrintArray(anArray, 9);
// Sort the array in ascending order using the Ascending() function
SelectionSort(anArray, 9, Ascending);
PrintArray(anArray, 9);
SelectionSort(anArray, 9, EvensFirst);
PrintArray(anArray, 9);
return 0;
}
Callback Functions***
????
98
The OO approach of callback is to let the client class inherit from and
implement an interface. In your code you simply hold an interface pointer and
call the interface methods through that pointer. The client program will create
his implementation object; assign its pointer to the interface pointer in your
class before the calling pointer in your class is reached.
However, if the client class is already finished and did not implement the
interface you want, you have to use a less OO approach. The client programmer
(or your do it for him) should write a separate matching class for the client
class, which inherit from the desired interface with the function you will call
back, which provides the service you need by manipulating client-class objects.
In your code you have a pointer to the interface and invoke the service provided
by the separate class.
The least OO approach is to use function pointers like the above example.
99
else
{
return 1;
}
}
};
Virtual functions are implemented behind the scenes using function pointers, so
you really are using function pointers--it just happens that the compiler makes the
work easier for you. Using polymorphism can be an appropriate strategy (for
instance, it's used by Java), but it does lead to the overhead of having to create an
object rather than simply pass in a function pointer.
Function overloading
Make the client life easier having more than one function with the same name
Based on argument to parameter matching and not the return type
No special linker is required as the call is resolved at compile time
Function names are unique for the linker; Made possible by name mangling /
decoration
Name mangling depends on the compiler so all translations to be compiled using
the same compiler
Overloading on return values not possible as you can call a function and ignore
the return value (that is, you can call the function for its side effects).
Overloaded functions should be in the same scope.
If client code calls an overloaded function, then compiler proceeds in the
following way: -
i. Searches for the exact match
ii. if a match is found, checks for the accessibility
For example if we have three overloaded members as follows
fun(double) //private
fun(int) //private
fun(char) //public
If fun(10) is called, there will be a compiler error; as compiler does not go back to
find the next match.
Type-safe Linkage
The C++ compiler refuses to declare a function automatically for you, so it’s likely
that you will include the appropriate header file. However, if for some reason you
still manage to misdeclare a function, either by declaring by hand or including the
wrong header file (perhaps one that is out of date), the name decoration provides a
safety net that is often referred to as type-safe linkage.
//Def.cpp {O}
//Function definition
void f(int) {}
100
// Function misdeclaration
void f(char);
int main() {
f(1); // Causes a linker error
}
Even though you can see that the function is actually f(int), the compiler doesn’t
know this because it was told – through an explicit declaration – that the function is
f(char). Thus, the compilation is successful
The linker will fail because the compiler decorates the names, the definition
becomes something like f_int, whereas the use of the function is f_char. When the
linker tries to resolve the reference to f_char, it can only find f_int, and it gives you
an error message.
const overloading:-
When you have an inspector method and a mutator method with the same name and
the same number and type of parameters — the methods differ only in that one is
const and the other is non-const
The subscript operator is a common use of const-overloading.
When you apply the subscript operator to a MyFredList object that is non-const, the
compiler will call the non-const subscript operator. Since that returns a normal
Fred&, you can both inspect and mutate the corresponding Fred object.
However when you apply the subscript operator to a const MyFredList object, the
compiler will call the const subscript operator. Since that returns a Fred const&, you
can inspect the corresponding Fred object, but you can't mutate/change it:
A copy constructor can be overloaded based on const overloading.
STL containers use const overloading :-
i. A const overloaded begin() and end() function to decide whether to return a
const_iterator or a normal iterator
ii. std::map::operator[] is also overloaded on const. The const version throws
an error if you try to reference an invalid key, but the non-const version does
an insert.
Function Overriding
101
For example:
virtual Base& operator= (const Base& s);
virtual Derived& operator= (const Base& s);
Operator overloading
Which operators cannot be overloaded and why
You can redefine or overload the function of most built-in operators in C++. These
operators can be overloaded globally or on a class-by-class basis. Overloaded
operators are implemented as functions and can be member functions or global
functions.
You can overload any of the following operators:
+ - * / % ^ & | ~
! = < > += -= *= /= %=
^= &= |= << >> <<= >>= == !=
<= >= && || ++ -- , ->* ->
() [] new delete new[] delete[]
You can overload both the unary and binary forms of the following operators:
+ - * &
Restrictions
102
pointer to member) resolution) conditional)
We can’t overload these relational operators (&&, || and , (comma) because they
support short circuit4 evaluation but we can’t do that if we overload these operators
We can’t change the precedence so the operators call c1 + c2 * c3 will be converted
as follows: -
(c1.operator+(c2)) .operator*(c3) //NO
c1.operator+((c2) .operator*(c3)) //YES
//Implementation
4
If an operator supports short circuit evaluation it means
i) Evaluation proceeds left to right
ii) Evaluation stops as soon as the result is known
103
With this operator function both P1 and P3 are valid in the client code so we
don’t need the earlier implementation of operator+
In this implementation we need to consider the following situations:-
In the above operator+ implementation we are retuning an object of Complex:-
1. Is it possible to it return by ref?
//NO; As it’s a local object
Explanation
i. Why Complex Object reference as const?
We don’t want to modify Complex object.
ii. Why returning ostream object by reference.
We want to allow cascading.
iii. Why not returning ostream object by const reference
We want to allow cascading and in that case we shall be modifying the
ostream object.
104
Overloading insertion and extraction operators (<< and >>)
These operators are implemented as friend functions.
Example:
friend ostream& operator<< (ostream&, const Complex&);
friend istream& operator>> (istream&, const Complex&);
return_type operator()(parameter_list);
Unlike all other overloaded operators, you can provide default arguments and
ellipses in the argument list for the function call operator.
Example
class A
{
public:
...
105
Type Conversion operators***
class Date
{
public:
// epoch date in UNIX
Date(int = 1, int = 1, int = 1970);
// return type implied; cannot be specified
operator int() const;
operator BDate() const;
Date operator++(int); //post
Date& operator++(); //Pre
friend ostream& operator<<(ostream&, const Date&);
friend bool operator<(const Date&, const Date&);
private:
int dd, mm, yy;
};
Date::Date(int dd, int mm, int yy): dd(dd), mm(mm), yy(yy)
{
}
Date::operator int() const
{
106
return dd;
}
Date::operator BDate() const
{
return BDate(dd, mm);
}
//Requires two objects and two copy ctor calls
Date Date::operator ++(int)
{
Date t(*this);
// logic to change to date
++dd;
return t;
}
//No overheads
Date& Date::operator ++()
{
// logic to change to date
++dd;
return *this;
}
ostream& operator<<(ostream& o, const Date& d)
{
return o << d.dd << "-" << d.mm << "-" << d.yy << endl;
}
bool operator<(const Date& l, const Date& r)
{
if(l.yy > r.yy)
return false;
else if(l.yy == r.yy && l.mm > r.mm)
return false;
else if(l.yy == r.yy && l.mm == r.mm && l.dd >= r.dd )
return false;
else
return true;
}
107
T t = x;
x = y;
y = t;
}
//To use mysort()T needs to support relational operator: <
template<typename T> void mysort(T x[], int n)
{
int i, j;
for(i = 0; i < n - 1; ++i)
for(j = i + 1; j < n; ++j)
if(x[j] < x[i])
myswap(x[i], x[j]);
}
108
If you have a class derived from a base class and try to initialize your base class
members in the derived constructor; you can’t do so (compiler error) even if the
base class members are public.
Also calling base class’s constructor explicitly in the derived constructor wouldn’t
help as that will create a temporary object.
Example
//showing the right way of calling base constructor
class Base
{
public:
Base(int xx, int yy) : x(xx), y(yy)
{
cout << "Ctor of Base" << endl;
}
Base (): x(11), y(22)
{
cout << "Default Ctor of Base" << endl;
}
~Base()
{
cout << "Dtor of Base" << endl;
}
int x, y;
};
class Derived : public Base
{
public:
//The Right way of calling base Ctor
Derived(int x1, int y1, int z1) : z(z1), P2D(x1, y1)
{
cout << "Ctor of derived" << endl;
cout << x << "\t" << y << "\t" << z << endl;
}
int z;
};
Constructor invocation order in the inheritance hierarchy:-
i. Client code creates an object of derived class
ii. Constructor of derived class is invoked.
iii. Initialization list of derived class constructor
iv. Constructors of base classes are invoked in the order of inheritance.
v. The member of the current class are initialize in the order of declaration
vi. Body of the derived class constructor.
vii. Back to client code
109
Class F contains member objects of type (Class E and Class D, declared in that
order)
Class F inherits the concrete class Class C
Class C inherits the abstract/pure virtual class Class B
Class B inherits the concrete class class A
If the destructor for object of type Class F is called, the following happen in the
below order:
110
C(){
cout << "Ctor C" << endl;
}
~C(){
cout << "Dtor C" << endl;
}
void f()
{
cout << "C::f" << endl;
}
};
Public inheritance
All public members of the base class are accessible as public members of the
derived class and all protected members of the base class are accessible as protected
members of the derived class (private members of the base are never accessible
unless friended)
111
Public inheritance models the subtyping relationship of object-oriented
programming: the derived class object IS-A base class object
References and pointers to a derived object (DerivedPtr) are expected to be usable
by any code that expects references or pointers to any of its public bases
Protected inheritance
All public and protected members of the base class are accessible as protected
members of the derived class (private members of the base are never accessible
unless friended)
Protected inheritance may be used for "controlled polymorphism": within the
members of Derived, as well as within the members of all further-derived classes,
the derived class IS-A base: references and pointers to Derived may be used where
references and pointers to Base are expected
Rarely useful. Used in boost::compressed_pair to derive from empty classes and
save memory using empty base class optimization (example below doesn't use
template to keep being at the point):
Private inheritance
All public and protected members of the base class are accessible as private
members of the derived class (private members of the base are never accessible
unless friended).
Private inheritance is commonly used in policy-based design, since policies are
usually empty classes, and using them as bases both enables static polymorphism
and leverages empty-base optimization
Private inheritance can also be used to implement the composition relationship (the
base class subobject is an implementation detail of the derived class object). Using
a member offers better encapsulation and is generally preferred unless the derived
class requires access to protected members (including constructors) of the base,
needs to override a virtual member of the base, needs the base to be constructed
before and destructed after some other base subobject, needs to share a virtual base
or needs to control the construction of a virtual base. Use of members to implement
composition is also not applicable in the case of multiple inheritance from a
parameter pack or when the identities of the base classes are determined at compile
time through template metaprogramming.
112
Similar to protected inheritance, private inheritance may also be used for controlled
polymorphism: within the members of the derived (but not within further-derived
classes), derived IS-A base.
The usage of the base class is only for implementing the derived class. Useful with
traits and if size matters (empty traits that only contain functions will make use of
the empty base class optimization). Often containment is the better solution, though.
The size for strings is critical, so it's an often seen usage here
Multlevel Inheritance
Example
Shape
TwoDimensional ThreeDimensional
Shape Shape
113
Cube
Multiple Inheritance
Deriving directly from more than one class is usually called multiple inheritance.
This concept complicates the design and debuggers can have a hard time with it,
Multiple Inheritance Example
If the derived classes (B & C) override the same method from the base
class when calling the method from the merged class (D) and the joining
class does also override that method, an ambiguity will rise.
A second problem that can occur with the diamond pattern is that if the
two classes derived from the same base class, and that base class has one or
more members, then those members will be duplicated in the joining class.
This problem is solved with virtual inheritance.
114
o Example :- iostream library objects std::cin and std::cout are both
implemented using multiple inheritance
Virtual inheritance***
Virtual inheritance is used in multiple inheritance, where a particular base class in
an inheritance hierarchy is declared to share its member data instances with any
other inclusions of that same base in further derived classes.
This can be used to avoid the diamond problem by clarifying ambiguity over which
ancestor class to use, as from the perspective of the deriving class (C in the example
above) the virtual base (X) acts as though it were the direct base class of C, not a
class derived indirectly through its base
Example
115
Virtual functions
Normal function calls are resolved at compile time; based on the type of the pointer
(or reference) and not based on the value of the pointer(or reference) whereas the
virtual function calls are resolved at run time; not based on type of pointer(or
reference) rather based on value of pointer(or reference).
Virtual function : Overheads
i. A table per class (Virtual table)
ii. A pointer per object (vptr)
iii. Extra dereferencing at runtime.
Derived class pointer (&DerObj) to base class pointer (pBase) is trivial conversion
but we can only access interface of Base class through pBase.
Can a virtual function be inline?
YES
Where in memory the virtual tables are stored?
May be Data Segment
If you have your code in a .h and .cpp file where the virtual table will be put?
As a rule the virtual table is put where the destructor is defined.
Templates
A template is a C++ entity that defines one of the following:
o A family of classes (class template), which may be nested classes
o A family of functions (function template), which may be member functions
o An alias to a family of types (alias template) (since C++11)
o A family of variables (variable template) (since C++14)
Templates are parametrized by one or more template parameters, of three kinds:-
o Type template parameters,
o Non-type template parameters, and
o Template template parameters.
A template can take one or more type parameters which are the symbols that
currently represent unspecified types.
For example:
116
Function templates result in overloading of functions. If algorithm is same, use
templates otherwise overloading.
Expansion per type and not per call
When template arguments are provided or deduced (for function templates only),
they are substituted for the template parameters to obtain a specialization of the
template, that is, a specific type or a specific function lvalue. Specializations may
also be provided explicitly: full specializations are allowed for both class and
function templates, partial specializations are only allowed for class templates.
Instantiation
o When a class/function template specialization is referenced, the template is
instantiated (the code for it is actually compiled), only if the template was
not already explicitly specialized or explicitly instantiated.
o Instantiation of a class template doesn't instantiate any of its member
functions unless they are also used.
o At link time, identical instantiations generated by different translation units
are merged.
A template is not compiled once to generate code usable for any type; instead, it is
compiled for each type or combination of types for which it is used. This leads to an
important problem in the handling of templates in practice: You must have the
implementation of a template function available when you call it, so that you can
compile the function for your specific type. Therefore, the only portable way of
using templates at the moment is to implement them in header files by using
inline functions5
5
To avoid the problem of templates having to be present in header files, the standard introduced a
template compilation model with the keyword export. However, I have not seen it implemented yet.
117
Templates are parametrized by one or more template parameters, of three kinds:-
o Type template parameters,
o Non-type template parameters, and
o Template template parameters.
Example:
The following limitations apply when instantiating templates that have non-type
template parameters:
integral or enumeration type :- The template argument provided
(expression or integral type) must be a converted constant expression of the
template parameter’s type (so certain implicit conversions apply)
Pointer to object: - The template arguments have to designate the address
of an object with static storage duration and a linkage (either internal or
external), or a constant expression that evaluates to the appropriate null
pointer or std::nullptr_t value.
Pointer to function: - The valid arguments are pointers to functions with
linkage (or constant expressions that evaluate to null pointer values).
lvalue reference to object or lvalue reference to function:- The argument
provided at instantiation cannot be a temporary, an unnamed lvalue, or a
named lvalue with no linkage
118
Pointer to member:- The argument has to be a pointer to member
expressed as &Class::Member or a constant expression that evaluates to null
pointer or std::nullptr_t value
In particular, this implies that these cannot be used as template arguments to
instantiate templates whose corresponding non-type template parameters are
pointers to objects.
o string literals,
o addresses of array elements, and
o addresses of non-static members
A template argument that can be used with a non-type template parameter
must be any converted constant expression, whose value can be determined at
compile time.
Such arguments must be:
o Constant expressions
o Addresses of functions or objects with external linkage, or
o Addresses of static class members
Non-constant expressions can't be parsed and substituted during compile-time. They
could change during runtime, which would require the generation of a new template
during runtime, which isn't possible because templates are a compile-time
concept.
Example
The value of integral argument should be known at compile time, this means
when being instantiated, only compile time constant integer can be passed. This
means 100, 100+99, 1<<3 etc. are allowed, since they are compiled time constant
expressions. Arguments, that involve function call, like abs (-120), are not allowed.
Types such as float, double and string literals are not allowed as non type template
parameters. Floats/doubles etc. may be allowed, if they can be converted to
integer.
Non-type template parameters of reference and pointer type cannot refer to/be the
address of
o A subobject (including non-static class member, base subobject, or array
element);
o A temporary object (including one created during reference initialization)
o A string literal
119
o The result of typeid
o The predefined variable __func__.
120
Template type arguments
A template argument for a type template parameter must be a type-id, which may
name an incomplete type:
121
Default argument for template parameter
Defaults can be specified for any kind of template parameter (type, non-type, or
template), but not to parameter packs
Default parameters are not allowed
o in the out-of-class definition of a member template (they have to be
provided in the declaration inside the class body)
o n friend class template declarations
On a friend function template declaration, default template arguments are allowed
only if the declaration is a definition, and no other declarations of this function
appear in this translation unit.
Default template arguments that appear in the declarations and the definition are
merged similarly to default function arguments:
But the same parameter cannot be given default arguments twice in the same scope
The template parameter lists of template template parameters can have their own
default arguments, which are only in effect where the template template parameter
itself is in scope:
122
Member access for the names used in a default template parameter is checked at the
declaration, not at the point of use:
The default template argument is implicitly instantiated when the value of that
default argument is needed, except if the template is used to name a function:
Class Templates
A class template describes a set of related classes or data types that differ only
By types,
By integral values,
By pointers or references to variables with global linkage, or
By a combination thereof
Class templates are particularly useful in describing generic, but type-safe, data
structures.
Classes, functions, variables, and member template specializations can be explicitly
instantiated from their templates. However, member functions, member classes, and
static data members of class templates can be explicitly instantiated from their
member definitions.
Only the declaration is required to be visible when explicitly instantiating a function
template, a variable template, a member function or static data member of a class
123
template, or a member function template. However, the complete definition must
appear before the explicit instantiation of a class template, a member class of a class
template, or a member class template, unless an explicit specialization with the
same template arguments appeared before.
If a function template, variable template, member function template, or member
function or static data member of a class template is explicitly instantiated with an
explicit instantiation definition, the template definition must be present in the same
translation unit.
When an explicit instantiation names a class template specialization, it serves as an
explicit instantiation of the same kind (declaration or definition) of each of its non-
inherited non-template members that has not been previously explicitly specialized
in the translation unit. If this explicit instantiation is a definition, it is also an
explicit instantiation definition only for the members that have been defined at this
point.
Explicit instantiation definitions ignore member access specifiers: parameter types
and return types may be private.
Implicit instantiation occurs when code refers to a template in context that
requires a completely defined type, or when the completeness of the type affects the
code, and this particular type has not been explicitly instantiated. For example,
when an object of this type is constructed, but not when a pointer to this type is
constructed.
This applies to the members of the class template: unless the member is used in the
program, it is not instantiated, and does not require a definition.
If a class template has been declared, but not defined, at the point of instantiation,
the instantiation yields an incomplete class type:
Local classes and any templates used in their members are instantiated as part of the
instantiation of the entity within which the local class or enumeration is declared.
124
Using Non-type (standard) parameters
Like, function templates, class templates also can have both type parameter and
Non-type parameter (expression parameters).
When an ordinary type is used as parameter, the template argument must be a
constant or a constant expression of an integral type.
The standard allows the below, for non-type template parameters
integral or enumeration type,
pointer to object or pointer to function,
lvalue reference to object or lvalue reference to function,
pointer to member,
std::nullptr_t.
Besides the constant expressions, the only other arguments allowed are: -
A pointer to a non-overloaded member,
Addresses of functions or objects with external linkage, or
Addresses of static class members.
For example you can make a fixed-size Stack by specifying a non-type parameter
as the dimension for the underlying array, as follows:-
The bitset class template is the only class in the STL that uses a Non-type template
parameter (which specifies the number of bits the bitset object can hold).
125
Nested Template Classes
Nested classes may also be templates:
Function template
A function template defines a family of functions.
Explicit instantiation
Specify a list of types
In the call before parentheses ()
126
Explicit instantiation of a function template or of a member function of a class
template cannot use inline or constexpr. If the declaration of the explicit
instantiation names an implicitly-declared special member function, the program is
ill-formed.
Explicit instantiation declarations do not suppress the implicit instantiation of inline
functions, auto-declarations, references, and class template specializations. (thus,
when the inline function that is a subject of explicit instantiation declaration is
ODR-used, it is implicitly instantiated for inlining, but its out-of-line copy is not
generated in this TU)
Explicit instantiation definition of a function template with default arguments is not
a use of the arguments, and does not attempt to initialize them:
Implicit instantiation
Based on match of arguments to parameters
Implicit instantiation occurs, when code refers to a function in context that requires
the function definition to exist, and this particular function has not been explicitly
instantiated. The list of template arguments does not have to be supplied if it can be
deduced from context
Example: -
127
Template argument deduction
In order to instantiate a function template, every template argument must be known,
but not every template argument has to be specified. When possible, the compiler
will deduce the missing template arguments from the function arguments. This
occurs when a function call is attempted and when an address of a function template
is taken.
Template argument deduction takes place after the function template name lookup
(which may involve argument-dependent lookup) and before overload resolution.
128
o template argument deduction
o default template arguments
o specified explicitly, which can be done in the following contexts:
in a function call expression
when an address of a function is taken
when a reference to function is initialized
when a pointer to member function is formed
in an explicit specialization
in an explicit instantiation
in a friend declaration
There is no way to explicitly specify template arguments to overloaded operators,
conversion functions, and constructors, because they are called without the use of
the function name.
The specified template arguments must match the template parameters in kind (i.e.,
type for type, non-type for non-type, and template for template)
The specified non-type arguments must either match the types of the corresponding
non-type template parameters, or be convertible to them.
The function parameters that do not participate in template argument deduction (e.g.
if the corresponding template arguments are explicitly specified) are subject to
implicit conversions to the type of the corresponding function parameter (as in the
usual overload resolution).
129
Function template specialization???
Explict Specialization
Even though a generic function overloads itself as needed, you can explicitly
overload one, too. This is formally called explicit specialization.
Explicit specialization allows customizing the template code for a given set of
template arguments.
If you overload a generic function, that overloaded function overrides (or "hides")
the generic function relative to that specific version.
Explicit specialization of a template allows you to tailor a version of a generic
function to accommodate a unique situation—perhaps to take advantage of some
performance boost that applies to only one type of data, for example.
Example
130
o class template
o variable template (since C++14)
o member function of a class template
o static data member of a class template
o member class of a class template
o member enumeration of a class template
o member class template of a class or class template
o member function template of a class or class template
Explicit specialization can only appear at namespace scope, in the same namespace
as the primary template (or in the nearest enclosing namespace if the primary is a
member template), and it has to appear after the non-specialized template
declaration. It is always in the scope of that namespace:
Specialization must be declared before the first use that would cause implicit
instantiation, in every translation unit where such use occurs:
A template specialization that was declared but not defined can be used just like any
other incomplete type (e.g. pointers and references to it may be used)
??? http://en.cppreference.com/w/cpp/language
131
------------------------------------------------------------
Function templates and non-template functions may be overloaded.
A non-template function is always distinct from a template specialization with the
same type.
Specializations of different function templates are always distinct from each other
even if they have the same type.
Two function templates with the same return type and the same parameter list are
distinct and can be distinguished with explicit template argument list.
When an expression that uses type or non-type template parameters appears in the
function parameter list or in the return type, that expression remains a part of the
function template signature for the purpose of overloading:
Example
When determining if two dependent expressions are equivalent, only the dependent
names involved are considered, not the results of name lookup. If multiple
declarations of the same template differ in the result of name lookup, the first such
declaration is used:
132
Two function templates are considered equivalent if
o they are declared in the same scope
o they have the same name
o they have identical template parameter lists
o the expressions involving template parameters in their return types and
parameter lists are equivalent
Two expressions involving template parameters are called functionally equivalent if
they are not equivalent, but for any given set of template arguments, the evaluation
of the two expressions results in the same value.
Two function templates are considered functionally equivalent if they are
equivalent, except that one or more expressions that involve template parameters in
their return types and parameter lists are functionally equivalent
If a program contains declarations of function templates that are functionally
equivalent but not equivalent, the program is ill-formed; no diagnostic is required.
When the same function template specialization matches more than one overloaded
function template (this often results from template argument deduction), partial
ordering of overloaded function templates is performed to select the best match.
Specifically, partial ordering takes place in the following situations:
o Overload resolution for a call to a function template specialization
???
o When the address of a function template specialization is taken
???
o When a placement operator delete that is a function template specialization
is selected to match a placement operator new
???
o When a friend function declaration, an explicit instantiation, or an explicit
specialization refers to a function template specialization
???
133
http://en.cppreference.com/w/cpp/language/function_template
------------------------------------------------------------
Template declarations (class, function, and variables (since C++14)) can appear
inside a member specification of any class, struct, or union that aren't local
classes.
Partial specializations of member template may appear both at class scope and at
enclosing namespace scope, but explicit specializations may only appear at
enclosing namespace scope.
If the enclosing class declaration is, in turn, a class template, when a member
template is defined outside of the class body, it takes two sets of template
parameters: one for the enclosing class, and another one for itself:
134
Member function templates
Member functions of classes may be templates
A non-template member function and a template member function with the same
name may be declared. In case of conflict (when some template specialization
matches the non-template function signature exactly), use of that name and type
refers to the non-template member unless an explicit template argument list is
supplied.
135
An out-of-class definition of a member function template must be equivalent to the
declaration inside the class (see function template overloading for the definition of
equivalency), otherwise it is considered to be an overload.
136
It would be an error to use different template types for the objects of the assign ()
operation even if an automatic type conversion from one type to the other is
provided:
By providing a different template type for the member function, you relax the rule
of exact match. The member template function argument may have any template
type, then as long as the types are assignable:
Note that the argument x of assign() now differs from the type of *this. Thus, you
can't access private and protected members of MyClass<> directly. Instead, you
have to use something like getValue() in this example.
Example:
137
STL Example
The structure pair is defined in <utility> as follows:
The template version of a copy constructor provided here is used when implicit type
conversions are necessary. If an object of type pair gets copied, the normal
implicitly generated default copy constructor is called
138
During overload resolution, specializations of conversion function templates are not
found by name lookup. Instead, all visible conversion function templates are
considered, and every specialization produced by template argument deduction
(which has special rules for conversion function templates) is used as if found by
name lookup.
Using-declarations in derived classes cannot refer to specializations of template
conversion functions from base classes.
A user-defined conversion function template cannot have a deduced return type
139
When used at class scope, variable template declares a static data member template.
As with other static members, a definition of a static data member template may be
required. Such definition is provided outside the class definition. A template
declaration of a static data member at namespace scope may also be a definition of
a non-template data member of a class template:
Template constructors
Template constructors are usually provided to enable implicit type conversions
when objects are copied
Note that a template constructor does not hide the implicit copy constructor. If the
type matches exactly, the implicit copy constructor is generated and called.
Thus, if you write a template constructor, don't forget to provide a copy constructor,
if the default copy constructor does not fit your needs
If you declare any constructor (including a template one), the compiler will refrain
from declaring a default constructor.
Unless you declare a copy-constructor (for class X one that takes X or X& or X
const &) the compiler will generate the default copy-constructor.
140
If you provide a template constructor (for class X which takes T const & or T or
T&) then the compiler will nevertheless generate a default non-template copy-
constructor, even though you may think that it shouldn't because when T = X the
declaration matches the copy-constructor declaration.
In the latter case you may want to provide a non-template copy-constructor along
with the template one. They will not conflict. When X is passed the non-template
will be called. Otherwise the template
A special form of a member template is a template constructor. Template
constructors are usually provided to enable implicit type conversions when objects
are copied. Note that a template constructor does not hide the implicit copy
constructor. If the type matches exactly, the implicit copy constructor is generated
and called.
For example
Here, the type of xd2 is the same as the type of xd, so it is initialized via the built-in
copy constructor. The type of xi differs from the type of xd, so it is initialized by
using the template constructor. Thus, if you write a template constructor, don't
forget to provide a copy constructor, if the default copy constructor does not fit
your needs.
Example
141
Template Specialization
Template specialization lets templates deal with special cases. Sometimes a generic
algorithm can work much more efficiently for a certain kind of sequence (for
example, when given random-access iterators), and so it makes sense to specialize it
for that case while using the slower but more generic approach for all other cases.
Performance is a common reason to specialize, but it's not the only one; for
example, you might also specialize a template to work with certain objects that
don't conform to the normal interface expected by the generic template.
These special cases can be handled using two forms of template specialization:
142
Any of the following can be fully specialized:
o function template
o class template
o variable template (since C++14)
o member function of a class template
o static data member of a class template
o member class of a class template
o member enumeration of a class template
o member class template of a class or class template
o member function template of a class or class template
Explicit specialization lets you write a specific implementation for a particular
combination of template parameters.
Example: - Explicit template specialization
In the above example, if the specialization of eq does not exist for the char* the last
call to eq() instantiate a char* version of the template function and always returns
that the x and y are not same which is a wrong result.
So we have added a specialized version of eq() to compare char*
Explicit specialization can only appear at namespace scope, in the same namespace
as the primary template (or in the nearest enclosing namespace if the primary is a
member template), and it has to appear after the non-specialized template
declaration. It is always in the scope of that namespace:
Specialization must be declared before the first use that would cause implicit
instantiation, in every translation unit where such use occurs:
143
A template specialization that was declared but not defined can be used just like any
other incomplete type (e.g. pointers and references to it may be used)
A function with the same name and the same argument list as a specialization is not
a specialization (see template overloading in function_template)
An explicit specialization of a function template is inline only if it is declared with
the inline specifier (or defined as deleted), it doesn't matter if the primary template
is inline.
Default function arguments cannot be specified in explicit specializations of
function templates, member function templates, and member functions of class
templates when the class is implicitly instantiated.
An explicit specialization cannot be a friend declaration.
If the primary template has a exception specification that isn't noexcept(false), the
explicit specializations must have a compatible exception specification.
Members of specializations
When defining a member of an explicitly specialized class template outside the
body of the class, the syntax template <> is not used, except if it's a member of an
explicitly specialized member class template, which is specialized as a class
template, because otherwise, the syntax would require such definition to begin with
template<parameters> required by the nested template
144
An explicit specialization of a static data member of a template is a definition if the
declaration includes an initializer; otherwise, it is a declaration. These definitions
must use braces for default initialization:
145
Member or a member template may be nested within many enclosing class
templates. In an explicit specialization for such a member, there's a template<> for
every enclosing class template that is explicitly specialized.
In such a nested declaration, some of the levels may remain unspecialized (except
that it can't specialize a class member template if its enclosing class is
unspecialized). For each of those levels, the declaration needs
template<arguments>, because such specializations are themselves templates:
146
Partial template specialization
Allows customizing class templates for a given category of template arguments.
For class templates only, you can define partial specializations that don't have to
fix all of the primary (unspecialized) class template's parameters
Syntax
Where className identifies the name of a previously declared class template. This
declaration must be in the same namespace or, for member templates, class scope as
the primary template definition which it specializes.
Example
147
Declarations 2 to 5 declare partial specializations of the primary template. The
compiler will then choose the appropriate template.
148
Name lookup
Partial template specializations are not found by name lookup. Only if the primary
template is found by name lookup, its partial specializations are considered. In
particular, a using declaration that makes a primary template visible, makes partial
specializations visible as well:
Partial ordering
When a class template is instantiated, and there are partial specializations available,
the compiler has to decide if the primary template is going to be used or one of its
partial specializations.
1. If only one specialization matches the template arguments, that
specialization is used
2. If more than one specialization matches, partial order rules are used to
determine which specialization is more specialized. The most specialized
specialization is used, if it is unique (if it is not unique, the program cannot
be compiled)
3. If no specializations match, the primary template is used
149
is a class template specialization with all the template arguments from the
second partial specialization.
The function templates are then ranked as if for function template overloading.
150
If a class template is a member of another class template, and it has partial
specializations, these specializations are members of the enclosing class template. If
the enclosing template is instantiated, the declarations of each member partial
specialization is instantiated as well (the same way declarations, but not definitions,
of all other members of a template are instantiated)
If the primary member template is explicitly (fully) specialized for a given
(implicit) specialization of the enclosing class template, the partial specializations
of the member template are ignored for this specialization of the enclosing class
template.
If a partial specialization of the member template is explicitly specialized for a
given (implicit) specialization of the enclosing class template, the primary member
template and its other partial specializations are still considered for this
specialization of the enclosing class template.
151
Function Template Overloading and specialization examples
Function template overloading isn't the same thing as specialization, but it's related
to specialization
Example: - Consider the following declarations:
152
a. This calls #10, because it's an exact match for #10 and such non-templates are
always preferred over templates
b. This calls #8, because f<int> is being explicitly requested.
c. This calls #3 (T is int), because that is the best match.
d. This calls #2 (T is complex<double>), because no other f can match.
e. This calls #1 (T1 is int, T2 is float). You might think that #9 is very close -- and
it is -- but a non-template function is preferred only if it is an exact match.
f. This one does call #9, because now #9 is an exact match and the non-template is
preferred.
g. This calls #6 (T is complex<double>), because #6 is the closest overload. #6
provides an overload of f where the second parameter is a pointer to the same
type as the first parameter.
h. This calls #7 (T is double), because #7 is the closest overload.
i. This calls #5 (T is double). #5 provides an overload of f where the first
parameter is a pointer to the same type as the second parameter.
j. Clearly (by now), we're asking for #4 (T is double).
k. Several other overloads are close, but only #1 fits the bill here (T1 is double, T2
is int*).
l. This calls #3 (T is int*), which is the closest overload even though some of the
others explicitly mention a pointer parameter.
Templates friends
Both function template and class template declarations may appear with the friend
specifier in any non-local class or class template (although only function templates
may be defined within the class or class template that is granting friendship). In this
case, every specialization of the template becomes a friend, whether it is implicitly
instantiated, partially specialized, or explicitly specialized.
153
Friend declarations cannot refer to partial specializations, but can refer to full
specializations:
154
Default template arguments are only allowed on template friend declarations if the
declaration is a definition and no other declarations of this function template appear
in this translation unit.
or the function template has to be declared as a template before the class body, in
which case the friend declaration within Foo<T> can refer to the full specialization
of operator<< for its T:
155
Example :- stream insertion and extraction operators are often declared as non-
member friends
156
Templates and multiple-file projects
From the point of view of the compiler, templates are not normal functions or
classes. They are compiled on demand, meaning that the code of a template
function is not compiled until an instantiation with specific template arguments is
required. At that moment, when an instantiation is required, the compiler generates
a function specifically for those arguments from the template.
When projects grow it is usual to split the code of a program in different source
code files. In these cases, the interface and implementation are generally separated.
Taking a library of functions as example, the interface generally consists of
declarations of the prototypes of all the functions that can be called. These are
generally declared in a "header file" with a .h extension, and the implementation
(the definition of these functions) is in an independent file with c++ code.
Because templates are compiled when required, this forces a restriction for multi-
file projects: the implementation (definition) of a template class or function must be
in the same file as its declaration. That means that we cannot separate the interface
in a separate header file, and that we must include both interface and
implementation in any file that uses the templates.
Since no code is generated until a template is instantiated when required, compilers
are prepared to allow the inclusion more than once of the same template file with
both declarations and definitions in a project without generating linkage errors.
Exception handling
Code that you want to monitor for exceptions must have been executed from within
a try block. (Functions called from within a try block may also throw an
exception.)
Exceptions that can be thrown by the monitored code are caught by a catch
statement, which immediately follows the try statement in which the exception was
thrown.
An exception can be thrown from outside the try block as long as it is thrown by a
function that is called from within try block.
A try block can be localized to a function. When this is the case, each time the
function is entered, the exception handling relative to that function is reset
You can restrict the type of exceptions that a function can throw outside of itself. In
fact, you can also prevent a function from throwing any exceptions whatsoever. To
accomplish these restrictions, you must add a throw clause to a function definition.
The general form of this is shown here:
ret-type func-name(arg-list) throw(type-list)
{
// ...
}
Attempting to throw an exception that is not supported by a function will cause the
standard library function unexpected() to be called. By default, this causes abort()
to be called, which causes abnormal program termination.
There are five runtime error messages associated with exceptions:
No handler for the exception
Unexpected exception thrown
An exception can only be re-thrown in a handler
157
During stack unwinding, a destructor must handle its own exception
Out of memory
When errors are detected at runtime, the error message displays the type of the
current exception and one of the five error messages. By default, the predefined
function terminate() is called, which then calls abort().
Fundamentals
Try block
A try block contains the code which may throw the exception, or contains the code
that calls the method which may throw the exception.
Throw
A throw keyword specifies an operand which can be of any type. The thrown
object can be a class object or just a character string:
The throw statement is actually passing information to the exception handler in the
catch block. This information can be either only be the type of the thrown object –
in many cases it is enough, or plus the extra information carried in the thrown
object.
Throw point
When an exception is thrown, the control leaves the try block and searches for a
catch block which can match this exception. If one catch block catches the
exception, the code included will be executed, then the control will resume to the
first statement after the last catch block. All codes between the throw point and the
last catch block will be skipped. The control has no way to return to the throw
point.
If the exception is not caught, the method call stack is unwound and control will
further more exit to the outer scope and continue searching the following catch
blocks. If it is already the most outer block such as main, method terminate is
called, which by default will call method abort.
Catch block
When exception happens, a temporary copy of the thrown object is created and
initialized. If the catch block has defined a parameter name of that type, this name
will be given to this temporary object and used to access the data members and
methods of the object. If the catch block does not have an parameter name, then the
thrown object’s internal information can not be accessed, and it is only used to
match the catch block. After handler finishes, the temporary object is destroyed.
What to throw
Throw exceptions by value, and catch them by (usually const) reference. You
need to throw exceptions by value because the exception object will be destructed
when it goes out of scope (at the end of the basic block where it is caught -
unless you re-throw the original exception).
If you throw a pointer to an allocated object, it will not be deleted and you will
leak memory.
One might consider throwing an exception by reference. You would never throw
a reference to an allocated object as you will leak. But you could consider throwing
158
a reference to a static object. You may not think you can throw a reference to a local
object but you can.
Some Programmers throw pointers to static C string constants, e.g. throw
"bailed out"; this is not so bad in itself, because it does not leak memory.
But in the long run it is hard to ensure that only static constants are thrown; the
catch clause cannot discern the difference between a string that was compiled in and
a string you allocated.
Do not try to get around this by deleting a pointer that you have caught, as you
might have the opposite problem - you will crash if someone throws you the
address of some static memory
What to catch
You should catch an exception by reference to avoid the slicing problem.
For example, when catching std::exception and its subclasses such as the ones
declared in <stdexcept>. If you catch std::exception by value and query its what()
method for a diagnostic message, you will likely get an unhelpful result like the
single word "exception". Had you caught it by reference you will get the intended
error message. It is helpful to subclass your own exception classes from
std::exception and override what() to provide custom diagnostics.
You should generally catch by const reference, as this aids compiler optimization,
unless you will be calling methods on the exception that are not declared const.
Catching by reference also avoids some overhead. Catching by value will not catch
the exception object itself, but a copy.
Summary
The exception object that is passed to the catch clause is a copy of the object
that was thrown. This is meant to avoid the problem that would be caused by
the thrown object being destructed is it goes out of scope because of the
exception that is throwing it. Throwing a reference will still create a copy. The
only way to avoid this copy is to throw a pointer, but then you are faced with
the issue of whether and how to delete it.
159
Exception Specification
An exception specification (also called throw list) is throw followed by a list of
types enclosed in "( )":
It can be placed after any method header, to restrict the kind of exceptions that can
be thrown by this method.
All derived-class types based on the throw list types can also be thrown.
If any exception not listed in the throw list is thrown, method unexpected is called,
which will call the method specified by method set_unexpected, or by default it
will call method terminate.
Method terminate will call the method specified by method set_terminate, or by
default it will call method abort.
Method set_unexpected and set_terminate take method names i.e. function pointer
as arguments. The methods must be with void return type and no arguments.
Method set_unexpected and set_terminate returns the name of the method last
called by terminate or unexpected. This enables the programmer to save the name
and use it later.
If the user-defined method specified in set_terminate or set_unexpected does not
exit the program, method abort will be automatically called to end the program
execution at last.
If you include exception "std::bad_exception" (derived from base class
"exception" whose prototype is in header file <exception>) in the throw list of a
method, when unexpected throw happens, method unexpected will throw
"bad_exception" instead of calling terminate by default or user-defined method
specified by method set_unexpected.
An empty throw list "throw" means that the method wouldn't throw any exception
without alerting method unexpected
Stack Unwinding
When an exception is thrown but not caught in a certain scope, the method call
stack is unwound, which means that the method which thrown the exception
terminates, all local objects destroyed, then control returns to the point where this
method was called.
If that calling point is in a try block, control will leave that try block and search the
following handlers. If that point is not in a try block or the search fails, stack
unwinding will happen again -- until it reaches main, and method terminate will be
called, as said before.
As a general result, if you don’t provide any exception handling code and exception
happens, the program will be terminated. But if you provide a proper exception
handling measure, the program can go on working.
Example
160
Exception Thrown from Constructor and Destructor
When an exception is thrown in a constructor, destructors for all the local objects
created as part of the object being constructed will be called.
If a destructor which is called to unwind the stack, throws out an exception, method
terminate will be called.
Normally you should avoid throwing an exception out of a constructor without
catching it and doing necessary clean up in the constructor first. A simple solution is
to use default constructor to create a very "safe" model of object, then use another
method which is typically called "Init" in many applications to initialize it.
When the exception is thrown from a constructor
i. The constructor is exited
ii. The dynamic memory allocated before the throw point, becomes memory
leak.
iii. The destructor of the class is not called, because a destructor is only called
for a fully constructed object, and because the constructor is half-way
terminated by the throwing of exception, the object is not fully constructed.
The correct way to deal with such situation is
i. To catch possible exception in the constructor, and in the catch block, delete
all dynamic allocated resources, then rethrow the exception again as a
notification
161
ii. Use auto_ptr< > to create local object; the destructor of auto_ptr< > will
delete the object pointed by the wrapped pointer when it goes out of scope.
If you pass the name of a user-defined function (which has void return type and no
arguments) as an argument to method "set_new_handler", whose header file is
<new>, then new will not throw the ”std::bad_alloc" exception. Instead it will call
that handler repeatedly until it successfully allocates memory. You can even
overload operator new and "set_new_handler" for the class in question.
All exceptions thrown from the language or the STL are derived from the base class
exception
The base class "exception", whose header file is <exception>, offers the method
"what" to access the appropriate error message.
162
Standard exception class hierarchy
163
An exception of class out_of_range is used to report that an argument
value is not in the expected range, such as when a wrong index is used in
an array-like collection or string
An exception of class domain_error is used to report a domain error. In
addition, for the I/O part of the library, a special exception class called
ios_base::failure is provided. It may be thrown when a stream changes
its state due to an error or end-of-file.
164
Input/Output Using Stream Classes (work on reducing the material)
(Especially class diagram and special functions like getch, getline and diff with normal
I/O functions)
Look into STL document
165
Standard Function Library
(Book Reference is “The Complete Reference C++ Chapter 25-31)
C++ defines two types of libraries.
o The first is the standard function library. This library consists of general-
purpose, stand-alone functions that are not part of any class. The function
library is inherited from C.
o The second library is the object oriented class library.
Mathematical functions
http://www.cplusplus.com/reference/cmath/
Utility Functions
(All except Dynamic memory management)
http://www.cplusplus.com/reference/cstdlib/
http://www.cplusplus.com/reference/csignal/
http://www.cplusplus.com/reference/cassert/
http://www.cplusplus.com/reference/cstdarg/
Wide-Character Functions
166
Special topics
Copy-swap Idiom
http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom
pImpl: Pointer-to-Implementation
Compile-time polymorphism
167
memcpy is evil
It's a C construct you should never use in C++. Never use memcpy() in C++ code
memcpy() operates on bits and bytes, not on objects.
It just looks ugly and forces you to be concerned with things you shouldn't need to
worry about, like the size of TBar.
It fails to take into account what's actually being stored in the objects you're
copying, leading to erroneous results, or even uglier code that takes the special
cases into account.
Example:
Consider an example assignment operator’s various implementation for a call TFoo
derived from TSuperFoo
Implementation 1:
The above code just copies the pointer values from one TFoo to the other. In other
words, it's an ugly way of doing
fBar1 = that.fBar1;
fBar2 = that.fBar2;
Implementation 2:
This would copy the data members of that's TBars into this's TBars,
so this and that retain their own separate TBar objects.
The problem is that it bypasses the assignment operator and copy constructor
for TBar, so if TBar has any owning pointers6 of its own, you have the same
6
Owning pointer is a concept to manage memory effectively. Every object in the runtime environment,
there is one and only one pointer to it through which the object can be deleted. This pointer is an "owning
pointer," and the object or function containing that pointer is the object's "owner." All other pointers to
the object are called "aliasing pointers." The owner of the object expects to delete the object; objects with
aliasing pointers don't.
168
problem. You'll also have problems if TBar owns locks or system resources that
need to be properly cleaned up or duplicated when you change the internal state of
the object. And, of course, if any of these pointers is NULL, you'll probably crash.
External linkage
The linkage type of a name specifies its visibility from other scopes and translation
units. A name with external linkage can be referred to from every translation unit of
the program. Examples of such names include ordinary functions that aren't
explicitly declared as static, global objects, const objects explicitly declared extern,
classes, enumerations and their enumerators, templates, namespaces, and so on.
Here are a few examples of names with external linkage:
Example:
int n; //global non-static, hence external linkage
class C
{
void f(); // member functions
static int n; // static data members
};
extern const K; //defined in a different translation unit
void func ();
namespace NS
{
class D{}; // qualified name NS::D has external linkage
}
enum DIR
{
Up,
Down
}
// DIR, Up, and Down have external linkage
Internal linkage
A name with internal linkage is visible only from within the translation unit in
which it was declared. A name declared in a namespace scope (that is, declared
globally or within a namespace) has internal linkage if it's the name of a static
object, a static function, a member of an anonymous union, a member of an
anonymous namespace, a typedef name, or a const object not declared extern. Here
are some examples of names with internal linkage:
static void f(); //a static function
static int q; //a static object declared in global scope
namespace //members of anonymous namespace
169
{
class C{};
int x;
}
const M=1000; //const object not declared extern
union
{ //members of an anonymous union
int xxx;
float yyyy;
};
typedef int I; // typedef names
No linkage
Names with no linkage are visible only in the scope in which they're declared. Such
names include local objects, local classes, and other local types. Put differently, any
name that has neither external linkage nor internal linkage has no linkage. Here are
some examples of such names:
int main()
{
class C{}; // C is a local class; has no linkage
int j; // local object not declared extern has no linkage
C c; // the object c has no linkage
enum Parity // local enum and enumerators
{
Even,
Odd
};
}
Array-Based I/O???
auto_ptrs
The auto_ptr template class is designed to help manage memory in a semi-
automatic way and prevent memory leaks when unexpected events such as
exceptions would otherwise have caused the normal cleanup code to be skipped.
An auto_ptr is a pointer that serves as owner of the object to which it refers (if any).
As a result, an object gets destroyed automatically when its auto_ptr gets destroyed.
A requirement of an auto_ptr is that its object has only one owner.
Class auto_ptr<> does not allow you to initialize an object with an ordinary pointer
by using the assignment syntax. Thus, you must initialize the auto_ptr directly by
using its value
170
The former creates a new object of type Y by using an explicit conversion from type
X, whereas the latter creates a new object of type Y by using an implicit conversion.
171
The transfer of ownership implies a special use for auto_ptrs; that is, functions
can use them to transfer ownership to other functions. This can occur in two
different ways:
1. A function can behave as a sink of data. This happens if an auto_ptr is passed as
an argument to the function by value. In this case, the parameter of the called
function gets ownership of the auto_ptr. Thus, if the function does not transfer it
again, the object gets deleted on function exit:
172
Whenever an auto_ptr is passed to this implementation of bad_print(), the
objects it owns (if any) are deleted. This is probably not the programmer's
intention and would result in fatal runtime errors:
173
According to the concept of auto_ptrs, it is possible to transfer ownership
into a function by using a constant reference. This is very dangerous because
people usually expect that an object won't get modified when you pass it as
a constant reference. Fortunately, there was a late design decision that
made auto_ptrs less dangerous. By some tricky implementation
techniques, transfer of ownership is not possible with constant references.
In fact, you can't change the ownership of any constant auto_ptr:
Example:
174
This solution makes auto_ptrs safer than they were before. Many interfaces
use constant references to get values that they copy internally. In fact, all
container classes of the C++ standard library behave this way, which might look
like the following:
175
All in all, constant auto_ptrs reduce the danger of an unintended transfer of
ownership. Whenever an object is passed via an auto_ptr, you can use a constant
auto_ptr to signal the end of the chain.
For a const auto_ptr, the const does not mean that you can't change the value
of the object the auto_ptr owns (if any). You can't change the ownership of
a constant auto_ptr; however, you can change the value of the object to
which it refers. For example:
auto_ptr as members:-
By using auto_ptrs within a class you can also avoid resource leaks.
If you use an auto_ptr as member instead of an ordinary pointer, you no
longer need a destructor because the auto_ptr object gets deleted with the
class object deletion.
In addition, an auto_ptr helps to avoid resource leaks that are caused by
exceptions that are thrown during the initialization of an object. Note that
destructors are called only if any construction is completed. So, if an
exception occurs inside a constructor, destructors are only called for objects
that have been fully constructed. This might result in a resource leak
Example:
176
177
Note, however, that although you can skip the destructor, you still have to
program the copy constructor and the assignment operator. By default,
both would try to transfer ownership, which is probably not the intention.
In addition, and as mentioned earlier, to avoid an unintended transfer of
ownership you should also use constant auto_ptrs here if the auto_ptr
should refer to the same object throughout its lifetime.
Misusing auto_ptrs:- auto_ptrs satisfy a certain need; namely, to avoid
resource leaks when exception handling is used. Unfortunately, the exact
behavior of auto_ptrs changed in the past and no other kind of smart pointers
are provided in the C++ standard library, so people tend to misuse auto_ptrs.
Here are some hints to help you use them correctly
o auto_ptrs cannot share ownership:- An auto_ptr must not refer to an
object that is owned by another auto_ptr (or other object). Otherwise, if the
first pointer deletes the object, the other pointer suddenly refers to a
destroyed object, and any further read or write access may result in disaster.
o auto_ptrs are not provided for arrays:- An auto_ptr is not allowed to refer
to arrays. This is because an auto_ptr calls delete instead of delete [] for
the object it owns. Note that there is no equivalent class in the C++
178
standard library that has the auto_ptr semantics for arrays. Instead, the
library provides several container classes to handle collections of data
o auto_ptrs are not "universal smart pointers”:- An auto_ptr is not
designed to solve other problems for which smart pointers might be useful.
In particular, they are not pointers for reference counting. (Pointers for
reference counting ensure that an object gets deleted only if the last of
several smart pointers that refer to that object gets destroyed.)
o auto_ptrs don't meet the requirements for container elements: - An
auto_ptr does not meet one of the most fundamental requirements for
elements of standard containers. That is, after a copy or an assignment
of an auto_ptr, source and sink are not equivalent. In fact, when an
auto_ptr is assigned or copied, the source auto_ptr gets modified because it
transfers its value rather than copying it. So you should not use an auto_ptr
as an element of a standard container. Fortunately, the design of the
language and library prevents this misuse from compiling in a standard
conforming environment.
179
180
Call m[1][2] is actually converted to m.operator[](1).operator[](2);
Why shouldn’t my Matrix class’s interface look like an array-of-array?
Some people build a Matrix class that has an operator[] that returns a reference to
an Array object (Proxy Class Object)(or perhaps to a raw array, shudder), and that
Array object has an operator[] that returns an element of the Matrix (e.g., a
reference to a double). Thus they access elements of the matrix using syntax like
m[i][j] rather than syntax like m(i,j).
The array-of-array solution obviously works, but it is less flexible than the
operator() approach. Specifically, there are easy performance tuning tricks that can
be done with the operator() approach that are more difficult in the [][] approach,
and therefore the [][] approach is more likely to lead to bad performance, at least in
some cases.
For example, the easiest way to implement the [][] approach is to use a physical
layout of the matrix as a dense matrix that is stored in row-major form (or is it
column-major; I can't ever remember). In contrast, the operator() approach totally
hides the physical layout of the matrix, and that can lead to better performance in
some cases.
Put it this way: the operator() approach is never worse than, and sometimes better
than, the [][] approach.
The operator() approach is never worse because it is easy to implement the dense,
row-major physical layout using the operator() approach, so when that
configuration happens to be the optimal layout from a performance standpoint, the
operator() approach is just as easy as the [][] approach (perhaps the operator()
approach is a tiny bit easier, but I won't quibble over minor nits).
The operator() approach is sometimes better because whenever the optimal layout
for a given application happens to be something other than dense, row-major, the
implementation is often significantly easier using the operator() approach compared
to the [][] approach.
As an example of when a physical layout makes a significant difference, a recent
project happened to access the matrix elements in columns (that is, the algorithm
accesses all the elements in one column, then the elements in another, etc.), and if
the physical layout is row-major, the accesses can "stride the cache". For example,
if the rows happen to be almost as big as the processor's cache size, the machine can
end up with a "cache miss" for almost every element access. In this particular
project, we got a 20% improvement in performance by changing the mapping from
the logical layout (row, column) to the physical layout (column, row).
Of course there are many examples of this sort of thing from numerical methods,
and sparse matrices are a whole other dimension on this issue. Since it is, in
general, easier to implement a sparse matrix or swap row/column ordering using the
operator() approach, the operator() approach loses nothing and may gain something
— it has no down-side and a potential up-side.
Reference counting???
181
Software Engineering with Inheritance
A derived class does not need to access the source code of the base class, but only
need the base class's object code. Therefore, independent software vendors (ISV)
can develop their own class libraries and provide clients with only object codes.
Modifications to a base class do not require derived classes to change, as long as the
public and protected interfaces of the base class remain unchanged. Derived classes
may, however, need to be recompiled.
Although in theory users do not have to know the source code of the inherited class,
in practice lots of programmers still seem reluctant to use something that they don't
know. On the other hand, when performance is a major concern, programmers may
want to see source code of classes they are inheriting from, so that they can tune the
code to meet their performance requirements.
A base class specifies commonality -- all classes derived from a base class inherit
the capabilities and interfaces of that base class. In the object oriented design
process, the designer looks for commonality and "factors it out" to form a base
class. Derived classes are then customized upon the base class.
In a object oriented system, classes are often closely related. So the best way is to
"factor out" common attributes and behaviors and place them in a base class, then
use inheritance to form derived classes
182
Preprocessor
Function macros
This would replace any occurrence of getmax followed by two arguments by the
replacement expression, but also replacing each argument by its identifier, exactly
as you would expect if it was a function:
Defined macros are not affected by block structure. A macro lasts until it is
undefined with the #undef preprocessor directive:
#define str(x) #x
cout << str(test);
183
The operator ## concatenates two arguments leaving no blank spaces between
them:
#define glue(a,b) a ## b
glue(c,out) << "test";
Because preprocessor replacements happen before any C++ syntax check, macro
definitions can be a tricky feature. But, be careful: code that relies heavily on
complicated macros become less readable, since the syntax expected is on many
occasions different from the normal expressions programmers expect in C++.
#elif TABLE_SIZE<50
#undef TABLE_SIZE
#define TABLE_SIZE 50
#else
#undef TABLE_SIZE
#define TABLE_SIZE 100
#endif
int table[TABLE_SIZE];
The behavior of #ifdef and #ifndef can also be achieved by using the special
operators defined and !defined respectively in any #if or #elif directive:
184
Conditional Compilation
When we compile a program and some error happens during the compiling process,
the compiler shows an error message with references to the name of the file where
the error happened and a line number, so it is easier to find the code generating the
error.
The #line directive allows us to control both things, the line numbers within the
code files as well as the file name that we want that appears when an error takes
place. Its format is:
This code will generate an error that will be shown as error in file "assigning
variable", line 20.
185
This directive aborts the compilation process when it is found, generating a compilation
error that can be specified as its parameter:
#ifndef __cplusplus
#error A C++ compiler is required!
#endif
This example aborts the compilation process if the macro name __cplusplus is not
defined (this macro name is defined by default in all C++ compilers).
Self containment***
If header file A makes use of a type defined in file B, normally it should include B
before using that type. Such a file can be called “self-contained”. Not all files are
“self-contained”. Some library header files are very comprehensive and contains
lots of type definitions. Different definitions may use other definitions defined in
other files. If we want such files to be self contained, we may virtually end up with
each header file including all other header files. Considering that most clients
include such a header file just to use one or two definitions in it, it will be a big
waste of space. Therefore, such a header may decide not to include some other
files. If you want to include use type A defined in file X, and type A uses type B
which is defined in file Y, it is your duty to insure that file Y is included in front of
file X:
Example:
186
Pragma directive (#pragma)
This directive is used to specify diverse options to the compiler. These options are
specific for the platform and the compiler you use. Consult the manual or the reference
of your compiler for more information on the possible parameters that you can define
with #pragma.
If the compiler does not support a specific argument for #pragma, it is ignored - no
syntax error is generated.
187
The following macro names are always defined (they all begin and end with two
underscore characters, _):
macro value
Integer value representing the current line in the source code file being
__LINE__
compiled.
A string literal containing the presumed name of the source file being
__FILE__
compiled.
A string literal in the form "Mmm dd yyyy" containing the date in
__DATE__
which the compilation process began.
A string literal in the form "hh:mm:ss" containing the time at which the
__TIME__
compilation process began.
An integer value. All C++ compilers have this constant defined to some
value. Its value depends on the version of the standard supported by the
compiler:
199711L: ISO C++ 1998/2003
__cplusplus 201103L: ISO C++ 2011
The following macros are optionally defined, generally depending on whether a feature
is available:
macro value
In C: if defined to 1, the implementation conforms
__STDC__ to the C standard.
In C++: Implementation defined.
In C:
199401L: ISO C 1990, Ammendment 1
199901L: ISO C 1999
__STDC_VERSION__
201112L: ISO C 2011
188
__STDCPP_THREADS__ 1 if the program can have more than one thread
Example
//standard macro names
#include <iostream>
using namespace std;
int main()
{
cout << "This is the line number " << __LINE__;
cout << " of file " << __FILE__ << ".\n";
cout << "Its compilation began " << __DATE__;
cout << " at " << __TIME__ << ".\n";
cout << "The compiler gives a __cplusplus value of " << __cplusplus;
return 0;
}
Preprocessor pitfalls***
???
Inline functions
To avoid the calling overhead of small functionality, the compiler can be instructed to
make them inline so that compiler can replace those function definition wherever those
are being called. This can be done by placing the keyword ‘inline’ while defining the
function.
Pros:-
It speeds up your program by avoiding function calling overhead.
It save overhead of variables push/pop on the stack, when function calling happens.
It save overhead of return call from a function.
It increases locality of reference by utilizing instruction cache.
By marking it as inline, you can put a function definition in a header file (i.e. it can
be included in multiple compilation unit, without the linker complaining)
Cons:-
It increases the executable size due to code expansion.
189
C++ inlining is resolved at compile time. Which means if you change the code of
the inlined function, you would need to recompile all the code using it to make sure
it will be updated
When used in a header, it makes your header file larger with information which
users don’t care.
As mentioned above it increases the executable size, which may cause thrashing in
memory. More number of page fault bringing down your program performance.
Sometimes not useful for example in embedded system where large executable size
is not preferred at all due to memory constraints.
Macros
Macros are C-style
No spaces between name of macro and brace.
Generic.
Macros have side effects.
It has two advantages: it can be applied to different types and it saves a method
call.
Its disadvantage is that there is no type checking.
A macro has no concept of the scoping i.e. to. there is simply no way to express
class scope in a macro
Inline functions
Likely efficient
bloating of code
request to inline the code at the point of call
type safe: The compiler will check for the proper use of the function argument
list and return value (performing any necessary conversions), something the
preprocessor is incapable of
no side effects
inline fn works same as the out of line fn
internal linkage
fn should be defined in the same translation
code exposed
It can be implemented as templates in C++ which has all the benefits of macros
and meanwhile performs type checking
You’ll almost always want to put inline definitions in a header file. When the
compiler sees such a definition, it puts the function type (the signature
combined with the return value) and the function body in its symbol table.
An inline function in a header file has a special status, since you must include
the header file containing the function and its definition in every file where the
function is used, but you don’t end up with multiple definition errors (however,
the definition must be identical in all places where the inline function is
included).
190
When inlining is effective the compiler holds the function type (that is, the
function prototype including the name and argument types, in combination with
the function return value) in its symbol table.
When the compiler sees that the inline’s function type and the function body
parses without error, the code for the function body is also brought into the
symbol table.
191
192
Nice C++ FAQs
class B {
public:
int f(int i) { cout << "f(int): "; return i+1; }
// ...
};
class D : public B {
public:
double f(double d) { cout << "f(double): "; return d+1.3; }
// ...
};
int main()
{
D* pd = new D;
cout << pd->f(2) << '\n';
cout << pd->f(2.3) << '\n';
}
In other words, there is no overload resolution between D and B. The compiler looks
into the scope of D, finds the single function "double f(double)" and calls it. It never
bothers with the (enclosing) scope of B. In C++, there is no overloading across scopes -
derived class scopes are not an exception to this general rule. (See D&E or TC++PL3
for details).
But what if I want to create an overload set of all my f() functions from my base and
derived class? That's easily done using a using-declaration:
class D : public B {
public:
using B::f; // make every f from B available
double f(double d) { cout << "f(double): "; return d+1.3; }
// ...
193
};
class Arena {
public:
void* allocate(size_t);
void deallocate(void*);
// ...
};
But how can we later delete those objects correctly? The reason that there is no built-in
"placement delete" to match placement new is that there is no general way of assuring
that it would be used correctly. Nothing in the C++ type system allows us to deduce that
p1 points to an object allocated in Arena a1. A pointer to any X allocated anywhere can
be assigned to p1.
However, sometimes the programmer does know, and there is a way:
194
Now, we can write:
destroy(p1,a1);
destroy(p2,a2);
destroy(p3,a3);
If an Arena keeps track of what objects it holds, you can even write destroy() to defend
itself against mistakes.
It is also possible to define a matching operator new() and operator delete() pairs for a
class hierarchy
Consider:
template<class Container>
void draw_all(Container& c)
{
for_each(c.begin(),c.end(),mem_fun(&Shape::draw));
}
If there is a type error, it will be in the resolution of the fairly complicated for_each()
call. For example, if the element type of the container is an int, then we get some kind
of obscure error related to the for_each() call (because we can't invoke Shape::draw()
for an int).
To catch such errors early, I can write:
template<class Container>
void draw_all(Container& c)
{
Shape* p = c.front(); // accept only containers of Shape*s
for_each(c.begin(),c.end(),mem_fun(&Shape::draw));
}
The initialization of the spurious variable "p" will trigger a comprehensible error
message from most current compilers. Tricks like this are common in all languages and
have to be developed for all novel constructs. In production code, I'd probably write
something like:
template<class Container>
void draw_all(Container& c)
{
typedef typename Container::value_type T;
Can_copy<T,Shape*>(); // accept containers of only Shape*s
for_each(c.begin(),c.end(),mem_fun(&Shape::draw));
}
195
This makes it clear that I'm making an assertion. The Can_copy template can be defined
like this:
template<class T1, class T2> struct Can_copy
{
static void constraints(T1 a, T2 b) { T2 c = a; b = a; }
Can_copy() { void(*p)(T1,T2) = constraints; }
};
Can_copy checks (at compile time) that a T1 can be assigned to a T2.
Can_copy<T,Shape*> checks that T is a Shape* or a pointer to a class publicly derived
from Shape or a type with a user-defined conversion to Shape*. Note that the definition
is close to minimal:
one line to name the constraints to be checked and the types for which to check them
one line to list the specific constraints checked (the constraints() function)
one line to provide a way to trigger the check (the constructor)
Note also that the definition has the desirable properties that
You can express constraints without declaring or copying variables, thus the writer of a
constraint doesn't have to make assumptions about how a type is initialized, whether
objects can be copied, destroyed, etc. (unless, of course, those are the properties being
tested by the constraint)
No code is generated for a constraint using current compilers
No macros are needed to define or use constraints
Current compilers give acceptable error messages for a failed constraint, including the
word "constraints" (to give the reader a clue), the name of the constraints, and the
specific error that caused the failure (e.g. "cannot initialize Shape* by double*")
So why is something like Can_copy() - or something even more elegant - not in the
language? D&E contains an analysis of the difficulties involved in expressing general
constraints for C++. Since then, many ideas have emerged for making these constraints
classes easier to write and still trigger good error messages. For example, I believe the
use of a pointer to function the way I do in Can_copy originates with Alex Stepanov
and Jeremy Siek. I don't think that Can_copy() is quite ready for standardization - it
needs more use. Also, different forms of constraints are in use in the C++ community;
there is not yet a consensus on exactly what form of constraints templates is the most
effective over a wide range of uses.
However, the idea is very general, more general than language facilities that have been
proposed and provided specifically for constraints checking. After all, when we write a
template we have the full expressive power of C++ available. Consider:
template<class T, class B> struct Derived_from {
static void constraints(T* p) { B* pb = p; }
Derived_from() { void(*p)(T*) = constraints; }
};
196
Can_compare() { void(*p)(T1,T2) = constraints; }
};
struct B { };
struct D : B { };
struct DD : D { };
struct X { };
int main()
{
Derived_from<D,B>();
Derived_from<DD,B>();
Derived_from<X,B>();
Derived_from<int,B>();
Derived_from<X,int>();
Can_compare<int,float>();
Can_compare<X,B>();
Can_multiply<int,float>();
Can_multiply<int,float,double>();
Can_multiply<B,X>();
Can_copy<D*,B*>();
Can_copy<D,B*>();
Can_copy<int,B*>();
}
197
void f(vector<Fruit*>& vf) // innocent Fruit manipulating function
{
vf.push_back(new Orange); // add orange to vector of fruit
}
void h()
{
f(v); // error: cannot pass a vector<Apple*> as a vector<Fruit*>
for (int i=0; i<v.size(); ++i) v[i]->apple_fct();
}
Had the call f(v) been legal, we would have had an Orange pretending to be an Apple.
An alternative language design decision would have been to allow the unsafe
conversion, but rely on dynamic checking. That would have required a run-time check
for each access to v's members, and h() would have had to throw an exception upon
encountering the last element of v.
int main()
{
cout << "Please enter a word:\n";
string s;
cin>>s;
cout << "You entered " << s << '\n';
}
Note that there is no explicit memory management and no fixed-sized buffer that you
could possibly overflow.
If you really need a whole line (and not just a single word) you can do this:
#include<iostream>
#include<string>
using namespace std;
int main()
{
cout << "Please enter a line:\n";
string s;
getline(cin,s);
cout << "You entered " << s << '\n';
}
void g(X& x)
{
x.f(); // X::f or Y::f or error?
}
This problem can be solved in several ways. At the time of standardization, it was not
obvious which way would be best.
199
#include<iostream>
#include<string>
#include<sstream>
using namespace std;
int main()
{
int i = 127;
string ss = itos(i);
const char* p = ss.c_str();
cout << ss << " " << p << "\n";
}
Naturally, this technique works for converting any type that you can output using << to
a string.
void f(int i)
{
/* ... */
}
200
int g(double d)
{
/* ... */
}
double h()
{
/* ... */
}
Note that C++ type rules, not C rules, are used. So you can't call function declared
``extern "C"'' with the wrong number of argument. For example:
// C++ code
const int c = 7;
201
int* q1 = &c; // error
int* q2 = (int*)&c; // ok (but *q2=2; is still invalid code and may fail)
int* q3 = static_cast<int*>(&c); // error: static_cast doesn't cast away const
int* q4 = const_cast<int*>(&c); // I really mean it
The idea is that conversions allowed by static_cast are somewhat less likely to lead to
errors than those that require reinterpret_cast. In principle, it is possible to use the result
of a static_cast without casting it back to its original type, whereas you should always
cast the result of a reinterpret_cast back to its original type before using it to ensure
portability.
void f()
{
prefix;
// ...
Return(10);
// ...
202
Return(x++);
//...
suffix;
}
Imagine being presented with that as a maintenance programmer; "hiding" the macros
in a header - as is not uncommon - makes this kind of "magic" harder to spot.
One of the most common subtle problems is that a function-style macro doesn't obey
the rules of function argument passing. For example:
However, the problem with the (presumably unintended) double evaluation of i++
remains.
And yes, I do know that there are things known as macros that doesn't suffer the
problems of C/C++ preprocessor macros. However, I have no ambitions for improving
C++ macros. Instead, I recommend the use of facilities from the C++ language proper,
such as inline functions, templates, constructors (for initialization), destructors (for
cleanup), exceptions (for exiting contexts), etc.
Is there is any reason to make the permissions on an overridden C++ virtual function
different from the base class? Is there any danger in doing so?
For example:
class base {
public:
virtual int foo(double) = 0;
}
203
Ans:
The problem is that the Base class methods are its way of declaring its interface. It is, in
essence saying, "These are the things you can do to objects of this class."
When in a Derived class you make something the Base had declared as public private,
you are taking something away. Now, even though a Derived object "is-a" Base object,
something that you should be able to do to a Base class object you cannot do to a
Derived class object, breaking the Liskov Substitution Prinicple
Will this cause a "technical" problem in your program? Maybe not. But it will probably
mean object of your classes won't behave in a way your users expect them to behave.
Ans:
You do get the surprising result that if you have a child, you can't call foo, but you can
cast it to a base and then call foo.
child *c = new child();
c->foo; // compile error (can't access private member)
static_cast<base *>(c)->foo(); // this is fine, but still calls the implementation in
child
Ans:
There's no technical problem, but you will end up with a situation where the publicly
available functions will depend upon whether you have a base or derived pointer.
This in my opinion would be weird and confusing.
Ans:
It can be very useful if you are using private inheritance - i.e. you want to reuse a
(customized) functionality of a base class, but not the interface.
Ans:
1. No technical problem, if you mean by technical as there being a hidden runtime
cost.
2. If you inherit base publically, you shouldn't do this. If you inherit via protected
or private, then this can help prevent using methods that don't make sense unless
you have a base pointer.
The code has two built-in pitfalls. First, if it executes in a member function for an
extern, static, or automatic object, the program will probably crash as soon as the delete
statement executes. There is no portable way for an object to tell that it was instantiated
on the heap, so the class cannot assert that its object is properly instantiated. Second,
when an object commits suicide this way, the using program might not know about its
demise. As far as the instantiating program is concerned, the object remains in scope
and continues to exist even though the object did itself in. Subsequent dereferencing of
the pointer can and usually does lead to disaster.
204
You should never do this. Since compiler does not know whether the object was
allocated on the stack or on the heap, "delete this" could cause a disaster.
Virtual functions are implemented using a table of function pointers, called the vtable.
There is one entry in the table per virtual function in the class. This table is created by
the constructor of the class. When a derived class is constructed, its base class is
constructed first which creates the vtable. If the derived class overrides any of the base
classes virtual functions, those entries in the vtable are overwritten by the derived class
constructor. This is why you should never call virtual functions from a constructor:
because the vtable entries for the object may not have been set up by the derived class
constructor yet, so you might end up calling base class implementations of those virtual
functions
Can you think of a situation where your program would crash without reaching the
breakpoint which you set at the beginning of main()?
C++ allows for dynamic initialization of global variables before main() is invoked. It is
possible that initialization of global will invoke some function. If this function crashes
the crash will occur before main() is entered.
It will turn off "name mangling" for func so that one can link to code compiled by a C
compiler.
They are
const
volatile
mutable
const keyword indicates that memory once initialized, should not be altered by a
program.
volatile keyword indicates that the value in the memory location can be altered even
though nothing in the program code modifies the contents. For example if you have a
pointer to hardware location that contains the time, where hardware changes the value
of this pointer variable and not the program. The intent of this keyword to improve the
optimization ability of the compiler.
mutable keyword indicates that particular member of a structure or class can be altered
even if a particular structure variable, class, or class member function is constant.
205
struct data
{
char name[80];
mutable double salary;
}
206
MORE NICE C++ FAQs (from C++ FAQs Book - MARSHALL CLINE)
???
207
Brainbench like Questions
extern
An extern declaration does not define the variable unless it is also initialized in
the same statement
Declaration
In the below code snippet the local x in main hides the global x before the local x's
initializer is considered. Therefore the local x is being initialized with itself (the
local unitialized variable)
int x = 5;
int main(int argc, char** argv)
{
int x = x;
return 0;
}
In the below code snippet the local x does not hide the global x until the end of the
declaration. A local name will hide a global after the end of the declaration but
before the beginning of initialization.
const int x = 5;
int main(int argc, char** argv)
{
int x[x];
int y = sizeof(x) / sizeof(int);
return 0;
}
You can have a class and a regular variable with the same name. However the class
name will be hidden and the elaborated-type-specifier must be used to access the
class name.
So the below code snippet is legal
int x = 5;
class x
{
};
return 0;
208
}
A template class name can’t be the same as any other name in the same declarative
region. This is also the case for typedef names.
So the below code snippet is not legal
int x = 5;
return 0;
}
Scope Exception
Function parameters are accessible in the try handler. Function local variables are
NOT accessible in the try handler.
So the variable x and e will be available in try block.
void foo(int x) try
{
int y = 2;
throw 1;
}
catch(int e)
{
}
Scope-if
The variable declared in the else-if did not exist before its declaration and can not
be used after the end of the else clause. Variables declared in conditions of if,else-
if,else structures can be used in all subsequent conditions and bodies of the if,else-
if,else structure.
So in the below code snippet the variable y will be accessible in lines 8-15 only.
1 int main(int argc, char** argv)
209
2 {
3
4 if ( argc > 10 )
5 {
6
7 }
8 else if (int y = argc - 1 )
9 {
10
11 }
12 else
13 {
14
15 }
16
17 return 0;
18 }
Class scope
struct tester
{
int array[SIZE];
enum
{
SIZE = 3
};
void size()
{
std::cout << sizeof(array) / sizeof(int);
}
};
210
t.size();
return 0;
}
Names defined at any point in a class are in scope in all member functions of the
class. Thus, in the below code snippet, the enum SIZE is in scope in the function
foo and hides the global variable SIZE. So the value printed will be 3.
#include <iostream>
struct tester
{
void foo()
{
std::cout << SIZE << std::endl;
}
enum
{
SIZE = 3
};
};
enum declarations
211
std::cout << y << std::endl;
return 0;
}
Namespace lookup
#include <iostream>
namespace standards
{
struct datastructure
{
};
void bar()
{
}
}
foo(ds);
bar();
return 0;
}
In the above code snippet the namespace 'standards' is searched for a function 'foo'
because its argument 'ds' is defined in that namespace. For function 'bar', no additional
namespaces are searched and the name is not found.
This is called koenig lookup or argument dependent name lookup.
Linkage
Static const has external linkage so can be accessed from another translation unit.
So in the below code snippet variable w and z can be accessed from another
translation unit.
int w = 1;
212
static int x = 2;
const int y = 3;
extern const int z = 4;
Static Storage
Variables with static storage duration are zero initialized. Note that x has static
storage duration even though the static keyword is not used. According to the
standard: "All objects which do not have dynamic storage duration, do not have
thread storage duration, and are not local have static storage duration"
#include <iostream>
int x;
int main()
{
int y;
std::cout << x << std::endl;
std::cout << y << std::endl;
return 0;
}
Standard Conversions
int* can be implicity converted to int const* -- 4.4. There is no implicit conversion
from int const* to int*. So in the below code snippet line 9 will not compile
1 int main()
213
2{
3 int a = 2;
4
5 int* b = &a;
6
7 int const* c = b;
8
9 b = c;
10
11 return 0;
12 }
Temporaries
In the below code snippet int() creates a temporary variable which is an rvalue. The
temporary variable that is created can not be assigned to since it is an rvalue. Thus
this code should not compile.
#include <iostream>
int main()
{
int x = int() = 3;
return 0;
}
Casts
Only const_cast (or c-style casts) can be used to cast away constness
In addition to c-style only static_cast can be used to cast an int to an enum
In addition to c-style only reinterpret_cast can be used to cast an int to a pointer or
a pointer to an int
delete
Deleting NULL pointers have no effect. Deleting a pointer to a base class which
points to a derived object is legal assuming the base destructor is virtual.
Deleting an array of objects using a base class pointer is undefined. (line 18 will
cause and undefined behaviour)
1 struct Foo
2{
3 virtual ~Foo() {}
4 };
5
6 struct Bar : public Foo
7{
8 };
9
10 int main(int argc, char** argv)
11 {
12 Foo* f = new Bar;
13 delete f;
14 f = 0;
15 delete f;
16
17 Foo* fa = new Bar[10];
18 delete [] fa;
19 fa = 0;
20 delete fa;
21
22 return 0;
23 }
215
Pointers comparison
Unary operator ++
If the first operand to the logical AND operator evaluates to false the second
operand is guaranteed not to evaluate. Note: the logical OR operator is guaranteed
not to evaluate the second operand if the first operand is true. Note: for the logical
AND and OR operators, all side effects of the first expression, except for
destruction of temporaries, happen before the second expression is evaluated.
The output of below code snippet will be 1
#include <iostream>
return 0;
}
Conditional operator
The 2nd and 3rd operands to the conditional operator must be of the same type. If
one of the two can be converted to the other, the conversion will occur. Note: if a
conversion exists for each of the operands into the other's type, the program is ill-
formed.
216
Any construct that could possibly be considered a declaration is a declaration. In
this example, the second line of main is interpreted as a function declaration in
preference to an object declaration with initialization.
#include <iostream>
struct Foo
{
Foo(int d) : x(d) {}
int x;
};
int main()
{
double x = 3.14;
Foo f( int(x) );
return 0;
}
Array declaration
Only the first constant expression in a multiple dimension array can be ommitted.
Therefore the declaration of the array in this example is ill-formed.
int array[3][] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}}; //Not legal
Overloading
There can only be one function with the same signature. Altering the cv
qualification of parameters does not change the function signature. Therefore the
two foo functions have the same signature and the program is ill-formed.
int foo(int x, int y)
{
return x+y;
}
Default argument
217
Parameters of a function cannot be used in default argument expressions.
int foo(int x, int y = x) //Not leagal
{
return x+y+1;
}
struct Foo
{
int m_x;
Foo(int x) : m_x(x) {}
A virtual function call uses the default arguments in the declaration of the virtual
function determined by the static type of the pointer or reference denoting the
object. An overriding function in a derived class does not acquire default arguments
from the function it overrides.
So In the below code snippet, the method B::foo is called but with the default
argument of 5 from A::foo.
#include <iostream>
struct A
{
virtual int foo(int x = 5)
{
return x * 2;
218
}
};
struct B : public A
{
int foo(int x = 10)
{
return x * 3;
}
};
return 0;
}
Initialization
class Foo
{
public:
char c;
static double sd;
double d;
int i;
};
219
std::cout << f.c + f.d + f.i << std::endl;
return 0;
}
Virtual functions
struct BS
{
BS()
{
std::cout << "Hello World" << std::endl;
}
unsigned int color;
};
struct BS
{
BS()
220
{
std::cout << "Hello World" << std::endl;
}
};
struct DR : public virtual mid1, public virtual mid2, public virtual mid3, public
mid4 { };
Virtual functions must have a unique "final overrider" that overrides all other
instances of that function in its inheritance hierarchy
In the below code snippet neither Box::print nor Sphere::print override each other,
so the condition is not met and the GeoDisc class is ill-formed.
#include <iostream>
struct Shape
{
virtual void print()
{
std::cout << "SHAPE" << std::endl;
}
virtual ~Shape() {}
};
221
};
s->print();
delete s;
return 0;
}
Access Control
Member Initialization
All sub-objects representing virtual base classes are initialized by the constructor of
the most derived class. If the constructor of the most derived class does not specify
a mem-initializer for a virtual base class V, then V's default construtor is called to
initialize the virtual base class subobject.
Thus the output of below code snippet will be 20000
#include <iostream>
struct Car
{
Car() : price(20000) {}
Car(double b) : price(b*1.1) {}
double price;
};
222
struct Prius : public Toyota
{
Prius(double b) : Toyota(b) {}
};
return 0;
}
Copying Objects
struct A
{
A() : val() {}
A(int v) : val(v) {}
A(A a) : val(a.val) {} //Out of memory compiler error
int val;
};
return 0;
}
The below code will not compile as the third line of main tries to initialize a3 with
a2, but A's copy constructor takes a non-const reference which violates a2's const
declaration.
#include <iostream>
struct A
{
A() : val(0) {}
223
A(int v) : val(v) {}
A(A& a) : val(a.val) {} //having a non const argument is valid for copy
constructor
int val;
};
return 0;
}
Function Overloading
In the below code snippet When calling global foo, the function is overloaded and
each foo is called once depending on the type of the argument. When calling the
member foo, Gateway::foo hides Computer::foo so Gateway::foo is called twice.
224
So the output is 2 + 4 + 16 + 16 = 38
#include <iostream>
int foo(int i)
{
return 2;
}
double foo(double d)
{
return 4.0;
}
struct Computer
{
int foo(int i)
{
return 8;
}
};
return 0;
}
Member Templates
A member function template can not be virtual so the below code snippet will
not compile
#include <iostream>
struct mybase
{
int x;
225
template <int RANGE>
virtual void print()
{
std::cout << RANGE + x + 1 << std::endl;
}
};
b->x = 1;
b->print<5>();
return 0;
}
int main()
int main(int argc, char* argv[])
int main(int argc, char* argv[], char* options[])
226
Exercises
227