-
In some languages like
Smalltalk
&Python
, typechecking is done@ Runtime
. -
C++
is a staticaly-typed language, and type-checking is done@ Compile time
.
Perhaps, the most important feature in C++
is CLASS
, which allows users to define their own type.
-
C++ built-in primitive Types
:Arithmetic Types
:Integral types
: Character & Boolean typesFloating-point types
:
void
:
C++ Arithmetic Types:
1. bool -------- Boolean ------------- NA
2. char -------- Character ----------- 8 bits
3. wchar_t ----- Wide Character ------ 16 bits
4. wchar16_t --- Unicode Character --- 16 bits
5. wchar32_t --- Unicode Character --- 32 bits
6. short ------- Short Integer ------- 16 bits
7. int --------- Integer ------------- 16 bits
8. long -------- Long Integer -------- 32 bits
9. long long --- Long Integer -------- 64 bits
10. float ---------- Single precision floating point ----- 6 significant digits (generally 32 bits)
11. double --------- Double precision floating point ----- 10 significant digits (generally 64 bits)
12. long double ---- Extended precision floating point --- 10 significant digits
These are the minimum required precision of float, double, long double.
But the actual bits depend on the compilers.
-
A
char
is guaranteed to be big enough to hold numeric values of the machine's basic character set. So,char
is same size as machine's singlebyte
. -
Except for
bool
andextended character types
; theintegral types
may be eitherSigned or Unsigned
. -
By default,
int, short, long, long long
aresigned
. -
Use
double
for floating-point computations -
float
usually does not have enough precision & -
Cost of double-precision calculation v/s single-precision is negligible. Infact in some machines, double-precision calculations are faster.
-
If we assign an out-of-range value for an object of
unsigned
type, the result is the remainder of value modulo the number of values the target type can hold. -
If we assigned an out-of-range value to an object of
signed
type, the result is undefined. The program might appear to work or crash or take garbage values. -
If we used both
unsigned
andint
in an arithmetic operation, then theint
is implictly converted tounsigned
.
unsigned u = 10;
int i = -42;
std::cout << i + i << endl; // prints -84
std::cout << u + i << endl; // if 32 bit ints; prints 4294967264
- Integer
literals
that begin with0
are calledoctal
. - Integer
literals
that begin with0x
or0X
are calledHexadecimal
.
20 // decimal
024 // octal
0x43 // hexadecimal
0X43 // hexadecimal
-
By default,
deciaml literals are signed
; whileoctal
andhexadecimal
literals can be either signed or unsigned. -
There are no literals of type
short
. -
Decimal
literals:int
long
long long
-
Octal
&Hexadecimal
literals:int
,unsigned int
long
,unsigned long
long long
,unsigned long long
-
By default, the
floating-point literal
has typedouble
.
'a' ----- a character literal
"a" ----- a string literal
- in C++, the compiler appends a
'\0'
to everystring literal
.
Escape Sequences in C++ :
newline --------- \n double quote ------ \"
vertical tab ---- \v single quote ------ \'
horizontal tab -- \h question mark ----- \?
backslash ------- \\ formfeed ---------- \f
backspace ------- \b carriage return --- \r
-
true
andfalse
are bool literals. -
nullptr
is a pointer literal. -
C++ programers tedn to refer to
variables
as variables or objects interchangeably. -
Default Variable Initialization :
- The value of an object of built-in type that is not explicitly initialized depends on where it is defined.
- Variables defined outside a function body are initialized to
zero
. - Variables defined inside a function body are
unitialized
. - Uninitialized objects of built-in type defined inside a function body have undefined value.
-
Declaration
: It makes a name known to the program. -
Definition
: It creates an associated entity. It provides name, type & storage and may also assign some initial value to the entity. -
Any Declaration that has an initializer is a definition.
-
A Declaration can involve only a single base type.
-
extern
: To obtain a declaration that is not definition we use the keywordextern
.- An
extern
that has an initializer is a Definition. - Its an ERROR to privide an initializer on an
extern
inside a function.
- An
extern int i; // it declares but doesnot define i.
int j; // it declares & defines j.
extern int k = 1234; // k = 1234; initlization overrides the extern keyword.
//---------------------------------
public void functionA(.....){
......
extern double my_double = 2.17; // ERROR; initialization not allowed on an extern inside a function
......
}
//---------------------------------
-
Variables must be defined exactly once but can be declared multiple times.
-
To use the same variable in multiple files, we must declare the variable in all files but define that variable in one-and-only-one file.
-
IDENTIFIERS
:- Can be composed of
letters
,digits
, &undersore character
. - Must begin with either
letter
orunderscore
. - User-defined identifiers cannot contain two
underscore
characters. - User-defined identifiers cannot begin with
underscore
followed by an UPPERCASE letter. - Identifiers defined outside a function may not begin with
underscore
.
- Can be composed of
C++ Keywords:
alignas else public unsigned
alignof enum register using
asm explicit reinterpret_cast
auto export return virtual
bool extern short void
break false signed volatile
case float sizeof wchar_t
catch for static while
char friend static_assert
char16_t goto static_cast
char32_t if struct
class inline switch
const int template
constexpr long this
const_cast mutable thread_local
continue namespace throw
decltype new true
default noexcept try
delete nullptr typedef
do operator typeid
double private typename
dynamic_cast protected union
//----------------------------------------------
C++ Alternative operator names:
and not
and_eq not_eq
bitand or
bitor or_eq
compl xor
not xor_eq
-
The global scope has no name. Hence when the scope operator(
::
) has an empty lefthand side, it is a request to fetch the name on the right-hand side from the global scope. -
COMPOUND TYPES:
- References
rvalue
lvalue
- Pointers
- References
-
References: A reference is NOT an object. It's just another name for an already existing object.
When we define a refernce, instead of copying the initializer's value, we bind the refernce to its initializer.
Once initialized, a reference remains bound to its initial object. There is no way to rebind a reference to refer to another object. As there is no way to rebind a reference,
references
must be initialized.
int ival = 217;
int &refVal = ival; // refVal refers to (is another name for) ival
int &refVal2; // ERROR. references must be initialized.
// REFERENCE IS JUST AN ALIAS
// After a reference is defined, all operations on that reference are actually operations on the object to which it is bound
refVal = 2; // assigns 2 to the object to which refVal is bound i.e. now, ival = 2;
int ii = refVal; // same as ii = ival;
int &refVal3 = refVal; // refVal3 refers to the object to which refVal is bound i.e. ival
int j = refVal3; // initializes j to same value as ival
- The type of reference and object must be the same.
- A plain reference can be bound only to an
object
; not to aliteral
oran expresion
. - But, we can bind a
const reference
to aliteral
.
int &refVal4 = 10; // ERROR. plain reference can't be bountd to a literal (10)
const int &ref = 217; // OK: We can bind a 'const' reference to a literal (217)
double m_dval = 3.14;
int &refVal5 = m_dval; // ERROR. Reference and object types must match. initializer must be an int type
- Because we can't change the value of the
const
object after we create it must be initialized.
const int i = get_size(); // OK. initialized at runtime
const int j = 32; // OK. initialized at compile time
const int k; // ERROR. k is uninitialized const
- To define a single instance of
const
variable that is accessible to other files, we use theextern
keyword on both its definition and declaration(s).
// file1.cpp
// define and initializes a const, extern keyword specifies that bufSize is accessible to other files
extern const int bufSize = func();
// file2.cpp
// here extern specifies that the definition of bufSize is not local its somewhere else
extern const int bufSize; // same bufSize as defined in file1.cpp
- Unlike ordinary references, a reference to
const
cannot be used to change the object's value.
const int bufSize = 217; // OK.
const int &ref1 = bufSize; // OK. ref1 is a refernce to const variable bufSize
int &ref2 = bufSize // ERROR. non-const reference can't refer to a const variable
ref1 = 2491; // ERROR. refrence to const can't change the object's value
- Binding a
reference to const
to a non-const
object is LEGAL; but the refernce can't be used to change the value of the non-const
object.
int i = 217;
int &ref1 = i; // ref1 is bound to 'i'. ref1 can be used to change the value of 'i'
const int &ref2 = i; // ref2 is bound to 'i'. BUT ref2 can be used to change value of i
// b'coz ref2 is a 'refernce to const'
ref1 = 2491; // OK. ref1 is not const. now, "i = 2491"
ref2 = 1020; // ERROR. ref2 is a 'refernce to const'. still "i = 2491"
- We can define pointers that point to either
const
or non-const
. - A pointer to
const
may not be used to change the object to which it points. - We may store the address of a
const
object only in a pointer toconst
. - Dereferncing a pointer gives the object to which the pointer points.
const double pi = 3.14; // OK. pi is a const double; its value can't be changed
double *ptr = π // ERROR. non-const pointer(ptr) can't point to a const object (pi)
const double *cptr = π // OK. only const pointers can store the address of const object
*cptr = 2.17; // ERROR. cant assign to *cptr
- EXCEPTION: We can use a pointer to
const
to point to a non-const
object but, can't change the object's value through the pointer.
double pi = 3.14; // OK. non-const object
const double *cptr = π // OK. but cant use cptr to change the value of pi
-
Like refernce to
const
, a pointer toconst
says nothing about whether the object to which it points is aconst
or not. -
Unlike references, pointers are
object
. So, pointers can beconst
too. -
const
pointers: Like any otherconst
objects, aconst pointer
must be initialized & once initialized its value cannot be changed.
int errNumb = 0;
int *const pErr = &errNumb; // pErr is a const pointer. pErr will always point to errNumb
const double pi = 3.14;
const double *const pip = π // pip is a const pointer to a const object
// neither the value of the object addressed by 'pip' nor the address stored in 'pip' can be changed
*pip = 2.17; // ERROR. pip points to a const object
if (*pErr){ // if the object to which pErr points (i.e. errNumb) is non-zero
.....
*pErr = 0; // OK
.....
}
top-level
&low-level
const
:top-level const
: Used to indicate that the pointer inself is aconst
.low-level const
: When a pointer refers to aconst
object, we refer to thatconst
as a low-levelconst
.- The distinction b/w top-level and low-level matters when we copy an object.
- When we copy an object, top-level
const
are ignored. - Wen we copy an object both objects must have same low-level
const
qualifications. Low-levelconst
are never ignored.
int i = 0;
int *const p1 = &i; // const is top-level
const int ci = 42; // const is top-level
const int *p2 = &ci; // const is low-level
const int *const p3 = p2; // rightmost const is top-level; leftmost const is low-level
const int &r = ci; // const is low-level
//----------------------------------------------------------
i = ci; // OK copying the value of ci; top-level const in 'ci' ignored
p2 = p3; // OK. pointed-to type matches; top-level const in 'ci' ignored
//----------------------------------
// When we copy p3 we can ignore its top-level const but not the fact that it points to a const type
// Thus, we can't use p3 to initialize p4 (which points to plain int)
int *p4 = p3; // ERROR: p3 has a low-level const but p4 doesn't
//-------------------
p2 = p3; // OK: both p2 and p3 points to the object of same const type
p2 = &i; // OK. we can convert int* to const int*
int &r = ci; // ERROR: 'ci' is a const; but r doesn't refer to const
const int &r2 = i; // OK: can bind const int refernce to a plain int
-
const
expressions:- An expression whose value cannot be chnaged.
- It value can be evaluated at compile time.
- A
const
object that is initialized from aconst
expression is also aconst
expression
-
Variables defined inside a function ordinarily are not stored at fixed address. Hence we can not use
constexpr
pointers to point to such variables. -
The address of any variable defined outside any function is a constant expression. So we can use
constexpr
pointers to point to such objects. -
When we specify a pointer in a
constexpr
declaration, theconstexpr
specifier applies to the pointer not the type to which the pointer points.
const int *p = nullptr; // p is a pointer to a const int
constexpr int *q = nullptr; // q is a const pointer to int
constexpr
imposes a top-levelconst
on the objects it defines
constexpr int *np = nullptr; // np is a const pointer to int that is a null
int j = 0;
constexpr int i = 217; // i is a const int
// i & j must be defined outside any function
constexpr const int *p1 = &i; // p1 is a const pointer to a const int i
constexpr int *p2 = &j; // p2 is a const pointer to an int j
- A type alias is a name that is a synonym for another type.
- Type alias can be defined in the following two ways:
typedef
keywordusing
keyword
typedef double wages; // wages is a synonym for double
typedef wages base, *p; // base is synonym for double, p is double*
using SI = Sales_Item; // SI is a synonym for Sales_Item
// A type alias is a type name and can appear wherever a type name can appear
wages hourly, weekly; // same as "double hourly, weekly"
SI item; // same as "Sales_Item item;
typedef char *pstring; // pstring is "pointer to char" type
const pstring cstr = 0; // cstr is a const of type 'pstring' i.e cstr a 'const pointer to a char'
const pstring *ps; // ps is pointer that points to a const pstring (i.e ps is a pointer that points to a constant 'pointer to char')
//-----------------------
const char *cstr = 0; // ERROR: wrong interpretation of "const pastring cstr = 0"
-
ERROR to interpret a declaration that uses a type alias by conceptually replacing the alias with its corresponding type.
-
auto
ordinarily ignore top-levelconst
. If we want the deduced type to have top-levelconst
we must say so explicitly. -
When we define several variables in the same statement, it's important to remeber that
reference
orpointer
is part of a particular declarator not part of the base type of declaration.
int i = 0;
const int ci =i; // OK: ci has "top-level const"
auto k = ci, &l = i; // OK: k is a "int" (top-level const are ignored while copying in auto), l is "int&"
auto &m = ci, *p = &ci // OK: m is a "const int", p is a pointer to a "const int"
auto &n = i, *p2 = &ci; // ERROR: type deduced from 'i' is 'int', but type deduced from '&ci' is 'const int'
decltype
:decltype
is the only context in which the variable defined asreference
is not treated as a synonym for the object to which it refers.- The compiler analyzes the expression to determine the type but doesnot evaluate the expression.
- If
r
is a refernce thendecltype(r)
is a refernce type too. dereference operator(*)
is an example of an expression for whichdecltype
returns areference
.- Enclosing the name of the variable in a paranthesis affects the type returned by
decltype
. - When we apply the
decltype
to a variable without any paranthesis, we get the type of the variable. - If we wrap the variable's name in one or more paranthesis, the compiler will evaluate the operand as an expression.
A variable is an expression if it can be the left-hand side of an assignment.
Hence, in such cases
decltype
returs areference
. decltype
of a paranthesized variable is always a reference.
decltype(f()) sum = a; // sum has a type that is returned by f()
const int ci = 0, &cj = ci;
decltype(ci) x = 0; // OK: x has type "const int"
decltype(cj) y = x; // OK: y is refernce of type "const int&" and is bound to x (a "const int")
decltype(cj) z; // ERROR: z is a refernce of type "const int&" and all refernces must be initialized
// decltype of a paranthesized variable is always a refernce
decltype((i)) d; // ERROR: d has a type "int&" and hence must be initialized
decltype(i) e; // OK: e is an (uninitialized) "int"
decltype((variable)) --------> reference (always)
decltype(variable) ----------> reference only if "variable is a reference"
-
We can define a class inside a function but such classes have limited functionalities, hence classes are ordinarily not defined inside functions.
-
There can be only one definition of class in any given source file.
-
If we use a class in several different files, the class' definition must be the same.
-
In order to make sure that the class definition is same in each file, classes are usually defined in
header files
. -
Typically classes are stored in headers whose name derives from the name of the class (like,
string
class is defined instring.h
header file......etc) -
Whenever a header is updated, the source files that use the headers must be recompiled to get the new or changed declarations.
-
The Preprocessor is a program that runs before the Compiler & changes the source text of our program.
-
Preprocessor variables are used to guard against multiple inclusions.
-
The
#define
directive takes a name and define that as a preprocessor variable. -
The preporcessor variables can have states:
defined
ornot defined
.#ifdef
istrue
if the preprocessor variable is defined.#ifndef
istrue
if the preprocessor variable is not defined. If the test is true then everything following#ifdef
or#ifndef
is processed upto the matching#endif
-
Preprocessor variables DO NOT respect C++ scoping rules.
-
Preprocessor variables are usually written in all UPPERCASE to avoid name clashes with other variables in the header file.
string
: A variable-length sequence of characters.vector
: It holds the variable-length sequence of objects of any given type.scope-operator (::)
It says that the Compiler should look in the scope of the left-hand operand for the name on the right-hand operand.- Headers should not use
using
declarations. If a header has ausing
declaration then, every program that #include that header file will get the sameusing
declaration. As a result, a program that did not intend to use a specified library name might encounter name conflicts.
string s1; // default initialization, s1 is an empty string
string s2 = s1; // copy initialization; s2 is a copy of s1
string s3 = "rustom potter"; // copy initialization; s3 is a copy of the string literal, not including the null '\0'
string s4(17, 's'); // direct initialization; s4 is sssssssssssssssss
-
Like the input and output operations of built-in types, the string operator returns their left-hand operand as their result.
-
Comparision rules for
string
:- Rule 1 : If two
string
s (s1, s2
) have different lengths and if every character in the shorterstring (s1)
is equal to the corresponding characters in the longerstring (s2)
thens1 is less than s2
. - Rule 2 : If any character at corresponding positions in the two strings (
s1, s2
) differs, then the result of string comparision is the result of comparing the first characters at which the strings (s1 & s2
) differ.
- Rule 1 : If two
-
When we add
string
andcharacter/string literal
, atleast one operand to each+
operator must bestring
type.
string s0 = "potter ";
string s5 = s0 + "! "; // OK:
string s6 = "Rustom" + "! "; // ERROR: Can't add two string-literals ("Rustom" + "! ")
string s7 = s0 + "Rustom" + "! "; // OK: (s0 + "Rustom") returns a string type
string s8 = "Rustom" + " " + s0; // ERROR: Can't add two string-literals ("Rustom" + " ")
-
The C++ library incorporates the C library.
- Headers in C have names of the form
name.h
. The C++ version of these headers are namedcname
(replacing the.h
and putting a prefixc
before thename
) - The names defined in
cname
headers are defined instd
namespace whereas those define din.h
versions are not
- Headers in C have names of the form
-
range
for
statement :
for (declaration : expression) statement;
- The body of range
for
must not change the size of the sequence over which it is iterating.
string m_str("Rustom Potter");
// range for statement to print each character in a string
for (auto m_char : m_str)
cout << m_char << endl;
cctype functions:
isalnum() isalpha() iscntrl() isdigit()
isgraph() islower() ispunct() isprint()
isspace() isupper() isxdigit()
tolower() toupper()
-
If we want to change the characters in a
string
, we must define the loop variables as areference type
-
The important part about
&&
operator is that it evalates the right-hand operand ONLY if the left-hand operand istrue
. -
Templates are not themselves classes or functions. Instead they can be thought of as instructions to the Compiler for generating classes or functions.
-
instantiation
: The process that the Compiler uses to create classes or functions from templates. -
When we use a template, we specify what kind of class or functions we want the Compiler to instantiate.
-
A
vector
is a class template. not atype
. -
vector
can hold objects of any type. -
Because,
references
are notobject
, we can't havevector
of references. -
We can add elements to
vector
@runtime. -
Most common way of using
vector
is to initially define an emptyvector
and then all elements @runtime
// In this example, the compiler generates 4 different types of vector templates
// vector<int>, vector<string>, vector<Sales_item>, vector<vector<int>>
vector<int> ivec; // vector of int; initially empty
vector<string> svec; // vector of string
vector<Sales_item> Sales_vec; // vector of Sales_item
vector<vector<int>> file; // vector whose elements are vectors of int
//----------------------------------------------
vector<int> ivec2(ivec); // OK: copy elements of 'ivec' into 'ivec2'
vector<int> ivec3 = ivec; // OK: copy elements of 'ivec' into 'ivec3'
vector<string>svec3 = ivec; // ERROR: svec3 holds string, ivec holds int
push_back
: It takes a value and "pushes" that value as a new element onto the back of thevector
.
vector<int> m_vec; // empty vector
for (int i = 0; i<100; ++i){
m_vec.push_back(i); // append sequential ints to vector m_vec
}
// at the end of the for-loop, m_vec will have int from 0 to 99
//-----------------------------------------
// read string from the input stream and store them in a vector
string m_str; // empty string
vector<string> m_strVec; // empty string vector
while(cin >> m_str){
m_strVec.push_back(m_str); // stores each string into the string-vector
}
-
We can't use range
for
, if the body of the loop adds elements to thevector
. -
It's an error to subscript (
[]
) an element that does not exist; but it is an error that the Compiler is unlikely to detect. Instead, the value we get @runtime is undefined. -
Buffer overflow errors are the result of subscripting elements that don't exist.
-
A good way to ensure that subscripts are in range is to avoid subscripting altogether by using a range
for
statement whenever possible. -
A valid
iterator
either denotes an element or denotes a position one past the last element in the container. All other iterator values are invalid. -
We can dereference a valid iterator to obtain the element denoted by the iterator.
-
Dereferencing an invalid iterator or and off-the-end iterator has undefined behaviour.
// Standard Container Iterator operations:
*iter --------------- Returns a reference to the element returned by the iterator "iter"
iter->mem --------------- Derefernces "iter" and fetches the member named "mem" from the
underlying element. equvalent to (*iter).mem
++iter --------------- Increments "iter" to refer to the next element in the container
--iter --------------- Decrements "iter" to refer to the previous element in the container
iter1 == iter2 --------------- Compares two iterators for equality.
iter1 != iter2 --------------- Compares two iterators for inequality.
Two iterators are equal if they denote the same element or if they
are the off-the-end iterator for the same container
- The library types that have iterators, define types named
iterator
andconst_iterator
that represent actual iterator types:
vector<int>::iterator iter1; // iter1 can read & write vector<int> elements
string::iterator iter2; // iter2 can read & write characters in a string
vector<int>::const_iterator iter3; // iter3 can ONLY read vector<int> elements
string::const_iterator iter4; // iter4 can ONLY read characters in a string
-
A
const_iterator
behaves like aconst
pointer. It can read but can't write the element it denotes. -
If a
vector
orstring
isconst
; we can use only itsconst_iterator
type. -
Each container class defines a type named
iterator
; thatiterator
type supports the actions of a (conceptual) iterator. -
The type returned by
begin
orend
depends on whether theobject
on which they operate isconst
or not. If the object isconst
thenbegin
andend
returnsconst_iterator
; if they are notconst
then they returniterator
. -
cbegin()
andcend()
: regardless of whether thevector
(or string) isconst
or not; they always returnconst_iterator
type.
vector<int> v;
const vector<int> cv;
auto iter1 = v.begin(); // iter1 has type vector<int>::iterator
auto iter2 = cv.begin(); // iter2 has type vector<int>::const_iterator
auto iter3 = v.cbegin(); // iter3 has type vector<int>::const_iterator
- When we derefernce a
iterator
, we get the object that theiterator
denotes.
vector<string> m_sVec;
//.....some initialization of m_sVec
//.....
// iter is a iteration into vector<string> m_sVec
auto iter = m_sVec.begin();
// derefernce and member access
(*iter).empty(); // OK: dereferences iter and calls the empty() member on the resulting object
*iter.empty(); // ERROR: attempts to fetch the member named empty on the resulting object
// but iter is an iterator and has no member named empty
->
operator: The->
(arrow) operator combines dereference and member-access into single operation.iter->mem
is equivalent to(*iter).mem
// Arrow operator (->) is equivalent to 'dereference & member-access'
iter->empty(); // OK: the ARROW-operator
(*iter).empty(); // OK: derefernce and member access
-
vector
operations that invalidate iterators:B'cozvector
s can grow dinamically:- we cannot add elements to
vector
inside a rangefor
loop. - any operation (like
push_back()
that changes size of a vector potentially invalidates all iterators into thatvector
.
- we cannot add elements to
-
So, loops that use iterators should not add elements to the container to which the iterator refers.
-
As with
vector
s,array
holdsobject
. There are no arrays of references. -
When we define an
array
we must specify a type for the array. We can't useauto
to deduce the type from the list of initializers. -
The dimension of the array must be known at Compile time i.e dimension must be a constant expression.
unsigned int cnt = 17;
string bad_Array[cnt]; // ERROR: "cnt" is not a constant expression
constexpr unsigned sz = 217; // constant expression
string good_Array[sz]; // OK: "sz" is a constant expression i.e its value will be same all the time
string m_array[get_size()]; // OK if get_size() is a constant expression; ERROR otherwise
- When we list-initialize an array, we can omit the dimension
const unsigned sz = 3;
int ia1[sz] = {0,1,2}; // OK: an array of 3 ints with elements {0,1,2}
int a2[] = {0,1,2}; // OK: an array of dimension 3 with elements {0, 1,2}
int a3[5] = {0,1,2}; // OK: same as a3[] = {0, 1, 2, 0, 0}
string a4[3] = {"Rustom", "Potter"}; // OK: same as a4[] = {"Rustom", "Potter", ""}
int a5[2] = {0,1,2}; // ERROR: too many initializers
- REMEMBER:
string
literals end with a null-character (\0
).
char a1[] = {'P', 'o', 't', 't', 'e', 'r'}; // Dimension = 6
char a2[] = {'P', 'o', 't', 't', 'e', 'r', '\0'}; // Dimension = 7 (explicit null-character)
char a3[] = "Potter"; // Dimesnion = 7 (null-character in string-literal is copied)
const char a4[6] = "Potter"; // ERROR: too many initializers (no room for including null-character in the string-literal)
- We cannot assign an
array
to anotherarray
. - We cannot use an
array
to initialize anotherarray
.
int a[] = {1,2,3}; // OK:
int a1[] = a; // ERROR: can't use another array to initialize an array
a1 = a; // ERROR: can't assign an array to another array.
- By default, type modifiers bind from right to left.
- In case of arrays, it can be easy to read array declarations from the inside out rather than right to left
int *parray[10]; // OK: parray is an array of 10 pointers-to-int
int &rarray[10] = /* ? */; // ERROR: reference is not an object & arrays can hold only objects
int (*Parray)[10] = &arr; //OK: Parray "points" to an array of 10 ints; Parray is a pointer
int (&Rarray)[10] = arra; //OK: Rarray "refers" to an array of 10 ints; Rarray is a reference
int *(&m_array)[10] = ptrs; //OK: m_array is a refernce to an array of 10 pointers-to-int; m_array is a refernce
int &(*m_array2)[10] = /* ? */; //ERROR: arrays cannot hold refernces (b'coz references are not objects)
-
When we use a variable to subscript an array, we normally should define that variable to have
size_t
type. -
size_t
is a machine-specificunsigned type
that is guaranteed to be large enough to hold the size of any object in memory. -
size_t
is defined incstddef
C++ header. Use#include <cstddef>
statement. -
In C++, when we use an
array
, the Compiler ordinarily converts the array to a pointer. -
When we use an
object
of array type, we are really using apointer
to the first element of the array. -
operations on
array
are actually operations onpointer
. -
When we use an
array
as an initializer for a variable defined usingauto
, the deduced type is apointer
not anarray
. But, if we usedecltype
then this issue doesn't arise.
int ia[] = {0,1,2,3,4,5,6,7,8,9}; // ia is an array of ints
auto ia2(ia); // ia2 has type int* (a pointer to ia[0] element)
// same as: auto ia2(&ia[0]);
ia2 = 217; // ERROR: can't assign an int to a pointer
//---------------------
int *ptr; // a pointer
decltype(ia) ia3 = {11, 12, 17, 223}; // ia3 is an array
ia3 = p; // ERROR: can't assign a pointer to an array
ia3[2] = 217; // OK: assigns 217 to the ia3[2] position
- Pointers to
array
elements support the same operations as iterators onvector
orstring
. - As with
iterators
, subtracting two pointers give us the distance b/w two pointers; but the pointers must point to the elements in the same array:
auto n = end(arr) - begin(arr); //OK:
auto m = end(arr) - begin(arr2); //ERROR if arr, arr2 are not same; can't subtract pointers from two different array
-
The result of subtracting two pointers is a library type named
ptrdiff_t
. -
ptrdiff_t
is asigned intergral type
and is machine-specific. -
Unlike subscripts for
vector
andstring
, the index of the built-in subscript operator is not anunsigned
type. -
We can't initialize an
array
from anotherarray
. -
We can't initialize an
array
from avector
. -
But, we can initialize a
vector
from anarray
.
int int_array[] = {0, 1, 2, 3, 4, 5};
vector<int> ivec(begin(int_array), end(int_array)); // ivec has same elements as int_array
vector<int> subVec(begin(int_array) + 1, begin(int_array)+4); // copies int_array[1], int_array[2], int_array[3]
-
ADVICE: Use library types instead of arrays. Modern C++ program should use
vector
,string
anditerators
instead of built-inarray
s andpointer
s -
Multidimensional Arrays
:- Strictly speaking, there are no multidimensional arrays in C++.
- Commonly, multidimensional array = (array of arrays).
-
lvalue
,rvalue
:lvalue
could stand on the left-hand side of the operand whilervalue
could not.- In C++, an
lvalue
expressions yields anobject
or afunction
.
-
When we use an object as:
lvalue
: we use object's identity (its location in memory).rvalue
: we use object's value (its contents).
-
We can use an
lvalue
when anrvalue
is required. -
But we cannot use a
rvalue
when anlvalue
(i.e a location) is required. -
Precedence & Associativity:
- Operands of operators with higher precedence group more tighly.
- Associativity guarantees how to group operands with same precedence.
-
The
arithmetic operators
are left-associative i.e. operators at same precedence group left-to-right. -
Precedence specifies how the operands are grouped, it says nothing about the order in which the operands are evaluated. In most cases, the orderis largely undefined.
-
The order of operand evaluation is determined by the Compiler to avoid pitfalls in programs.
int A = f1() * f2();
// We know that f1() and f2() are called before the multiplication is carried.
// But we don't know whether f1() is called first or f2(). The behaviour of calling is undefined.
- For operators that donot specify evaluation order, its an
ERROR
for an expression to refer to and change the same object.
int i = 0;
cout << i << ", " << ++i << endl; // ERROR: UNDEFINED behaviour
// B'coz the Compiler might evaluate ++i before i or otherwise.
// B'coz this code has undefine behaviour the program is an error irrespective of what the compiler compiles
-
Operators
that guarantee the order in which the operands are evaluated:- logical AND-operator (
&&
) : left-operand is evaluated first. right-operand is evaluated only if left-operand istrue
- logical OR-operator (
||
) - conditional-operator (
? :
) - comma-operator (
,
)
- logical AND-operator (
-
ADVICE: If you change an operand ; don't use the same operand elsewhere in the same expression.
-
The operands to
modulo-operator (%)
must have integral type. -
Except for when (
-m
) overflows; the following rules holds true always:(-m) / n
andm / (-n)
are both equal to-(m / n)
(-m) % n
is equal to-(m % n)
m % (-n)
is equal tom % n
-
Relational Operators take operands of
arithmetic
orpointer
types. -
Logical Operators take operands of any type that can be converted to
bool
. -
The
logical AND (&&)
andlogical OR (||)
operators always evaluate their left-hand operand first. Moreover, the right-hand operand is evaluated iff the left-hand operand does not determine the result. This strategy is calledshort-circuit evaluation
. -
short-circuit evaluation :
- The right side of
&&
is evaluated only if left side istrue
. - The right side of
||
is evaluated only if left side isfalse
.
- The right side of
-
It's a bad idea to use the boolean literals
true
andfalse
as operands in a comparision. These literals should be used only to compare to an object ofbool
type. -
The left-hand operand of the
assignment operator ( = )
must be a modifiablelvalue
. -
The
assignment operator ( = )
is right-associative. -
Compound assignment :
+= , -= , *= , /= , %= // Arithmetic operators
<<= , >>= , &= , ^= , |= // bitwise operators
-
When we use compound assignment, the left-hand operand is evaluated only once. But, if we use ordinary assignment, the left-hand operand is evaluated twice (once in the operation on right-hand side and again as the operand on the left-hand side). Apart from performance issue there is no b ig deal about using either.
-
prefix (
++i
)- yields the incremented value
- returns the
object
itself as anlvalue
-
postfix (
i++
)- yields the original (unincremented) value
- returns a copy of the object's original value as an
rvalue
-
The precedence of
postfix increment (i++)
is higher than that ofderefernce
operator. -
*ptr++
is equivalent to*(ptr++)
. This expression first computesptr++
and then yields the copy of the previous value ofptr
. -
Member Access :
dot operator (.)
arrow operator (->)
-
The
arrow operator (->)
requires a pointer operand and yields anlvalue
. -
The
dot operator (.)
yields anlvalue
if the object from which the member is fetched is anlvalue
; otherwise it yields anrvalue
. -
Conditional Operator
(?:)
:cond ? expr1 : expr2
- The result of
conditional operator (?:)
islvalue
if both operators arelvalue
or if they can convert to a commonlvalue
type. Otherwise the result is anrvalue
.
- The result of
-
switch
statement :case labels
must beintegral constant expressions
- ERROR for any two
case labels
to have the same value label
may not stand alone; it must precede a statement or another case label- When execution jumps to a particular
case
, any code that occured inside theswitch
before that label is ignored.
char ch = getVal();
int ival = 217;
const int cival = 17;
const double cdval = 2.17;
switch (ch){
case ival : // ERROR : ival is not a constant expression
case 2.1798 : // ERROR : non-integer case labels not allowed
case cdval : // ERROR : non-integer case labels not allowed
case 2 : // OK : integer literals are allowed
case 'potter' : // OK : constant expressions allowed
case cival : // OK : 'const int' allowed
// ..........
default :
}
-
Execution flows across
case labels
. After acase label
is matched, execution starts at that level and continues across all remaining cases or untill the program explicitly interrupts it usingbreak
statement. -
B'coz C++ is free-form; case labels need not appear on a new line.
switch (ch) {
case 'a' : case 'e' : case 'i' : case : 'o' case 'u' :
++vowelCount;
break;
}
// same as above code
switch (ch) {
case 'a' :
case 'e' :
case 'i' :
case 'o' :
case 'u' :
++vowelCount;
break;
}
-
Iterative Statements:
while
: tests condition before execution- Variables defined in a
while
condition orwhile
body are created and destroyed on each iteration.
- Variables defined in a
for
: tests condition before execution- init-statements can define several objects. However, init-statement can be only a single declaration statement; so all the variables must have the same base type.
- Omitting condition is equivalent to writing
true
as a condition.
- range
for
statement : do while
: executes then tests condition- condition cannot be empty
- Variables used in condition must be defined outside the
do while
statement
-
Range
for
statement:expression
must represent a sequence such as : braced initializer list, anarray
, or an object of type such asvector
orstring
that hasbegin()
andend()
members that returniterators
.- declaration defines a variable. It must be possible to convert each element of the sequence t
variable
's type. Prefer to useauto
in such cases. - If we want to write to the elements of the sequence, the loop variable must be a
reference
type. - We cannot use range
for
to add/remove elements ofvector
(or any other container).
// Range for loop
for (declaration : expression){
statement;
}
vector<int> iVec = {1,2,3,4,5,6,7,8,9};
// Range for loop
for(auto &ref : iVec){
ref *= 2; // multiplying the elements of the vector by 2; need to define the loop-variable as a reference
}
// Same as ablove code
for (auto beg = iVec.begin(), end = iVec.end(); beg != end; ++beg){
auto &ref = *beg; // ref must be a reference so we can change the element of iVec
ref *= 2;
}
- We cannot use range
for
to add/remove elements ofvector
(or any other container) because in arange for
the value ofend()
is cached and adding/removing elements might invalidate the theend
value.
do {
//........
mumble(foo);
} while (int foo = get_foo()); // ERROR : declaration of 'foo'(which is used inside the statement) cannot be done in condition
-
** Jump statements** :They interrupt the flow of execution
break
continue
goto
return
-
break
statement terminates nearest enclosingwhile
,do while
,for
orswitch
statements. -
A
break
can appear only within an iteration statement orswitch
statement.
string buf;
while(cin >> buf && !buf.empty()){
switch (buf[0]){
case '-':
for(auto iter = buf.begin()+1; iter != buf.end(); ++iter){
if(*iter == ' '){
break; // break #1, terminates the for loop
}
//.......
}
// break #1 transfers control here
//.........
//.......
break; // break #2, terminates the switch loop
case '+':
//.........
case '/':
//.........
}
// break #2 transfers control here
}
-
A
continue
statement terminates the current iteration of the nearest enclosing loop and immediately begins the next iteration. -
A
continue
statement can appear only infor
,while
ordo while
loop. -
Unlike a
break
, acontinue
may appear inside aswitch
block only if thatswitch
is embedded inside an iterative statement. -
Impacts of
continue
inwhile
ordo while
loop: next iteration continues by evaluating the condition- Traditional
for
loop: next iteration continues by evaluating theexpression
in thefor
-header - Range
for
loop: next iteration continues by initializing the control variable from the next element in the sequence
// Only words that begin with an underscore (_) will be processed
string buf;
while(cin >> buf && !buf.empty()){
if(buf[0] != '_'){
continue; // read the next inout from I/O stream
}
// still here? means the word starts with an underscore (_)....process buf
//..........
//......
}
- A
goto
statement provides an unconditional jump from thegoto
to another labeled-statement in the same function
goto label; // label is an idenitifer that identifies the satatement
- labeled statement : Any statement that is preceded by an identifier followed by
colon ( : )
label: return; // labeled statement; label = label
end: return; // labeled statement; label = end
-
label identifiers
are independent of the names used for variables and other identifiers -
Similar to
switch
statement, agoto
cannot tranfer control from a point where an initialized variable is out-of scope to a point where that variable is in scope. -
Jumping backwards to a point before a variable is defined; destroys the variable and constructs it again.
-
Exceptions
arerun-time
anomalies. -
runtime_error
is a standard library eception and is defined in thestdexcept
header -
As with any block, the
variables
declared inside thetry
block are inaccessible outside the block -- in particular they are not accessible to thecatch
clauses. -
Each of the library exception classes defines a member function named
what()
-
If no appropriate
catch
is found then the execution is transferred to a library function namedterminate
which is guaranteed to stop further execution of the program. -
If a program has no
try
blocks and an exception occurs thenterminate
is called and the program is exited.
-
Functions may be overloaded, meaning the same name may refer to several different functions.
-
A
function
's call does two things:- Initializes the function's parameters with the corresponding arguments
- Transfers control to that function
- Execution of the calling function is suspended(not terminated) and execution of the called function begins
-
The
return
statement does two things:- Return the value
- Transfers the control out of the called function to the calling function
-
Although we know which argument initializes which parameter; the order in which the arguments are evaluated completely depends on the Compiler.
-
The type of the
argument
andparameter
must match (or be convertible to the required type). -
Local variables at the outermost scope of the
function
may not use the same name as any parameter. -
The return type of a function may not be an
array
type or afunction
type. However, it may return apointer
-to-array orpointer
-to-function. -
In C++,
name
has scope andobject
has lifetime -
The scope of a
name
is the part of the program's text in which thatname
is visible. -
The lifetime of an
object
is the time during the program's execution that the object exists. -
local variables
:- Parameters and variables defined inside a function body are called local variables
- They hide declarations of the same name in an outer space
- The lifetime of a local variable depends on how it is defined
-
Objects defined outside any function exist throughout the program's execution.
-
Automatic Objects :
- Objects that exist only while a block is executing are known as automatic objects
- After execution exits a block, the value of the automatic objects created in that block is undefined
- Parameters are automatic objects.
- Automatic objects corresponding to local variables are initialized if their definition contains an initializer; otherwise it is default initialized.
-
local
static
objects:- local variables whose lifetime continues across calls to the function
- Each local
static
object is initialized before the first time the execution passes through the object's definition. - Local
static
objects are NOT destroyed when the function ends; they are destroyed when the program terminates. - local
static
objects of build-in tyes are initialized tozero (0)
.
-
Reference parameters
allow afunction
to change the value of one or more of itsarguments
. -
Reference parameters
that are not changed inside a function should be references toconst
. -
We can pass either a
const
or a non-const
object to a parameter that has a top-levelconst
-
top-level
const
on parameters are ignored -
We cannot pass a
const
object, or aliteral
, or an object that requires conversion to a plain reference type. -
We cannot copy an
array
. So, we cannot pass an array by value. -
When we use an
array
, it is (usually) converted to apointer
. So, when we pass an array to a function we are actually passing a pointer to the array's first element.
// despite appearances all the three declarations of 'func' are equivalent
// each function has a single parameter of type 'int *'
// when the Compiler checks a call to 'func', it only checks that the argument has type 'const int*'
void func(const int *);
void func(const int[]);
void func(const int[10]);
int i = 0, j[2] = {1, 2};
func(&i); // OK: &i is a (int *)
func(j); // OK: j is converted to (int *) that points to j[0]
-
If we pass an array to a function that
argument
is automatically converted to apointer-to-the-first-element
in the array; the size of the array is irrelevant. -
Functions with varying
parameters
: C++ has following ways to write a function that takes varying no. of arguments:- If all the arguments have the same type, we can pass a library type named
initializer_list
. - If the arguments' type vary we can write a special type of function, known as variadic template
- ellipsis (this facility is normally used in programs that need to interface with C programs)
- If all the arguments have the same type, we can pass a library type named
-
Calls to
functions
that returnreferences
arelvalues
; other return types arervalues
. -
B'coz functions that return references actually return
lvalues
, we can assign to the result of a function that returns areference
-to-nonconst
. -
B'coz we cannot copy an array; a function cannot return an array but can return a pointer or refernce to array.
-
Functions that have the same name but different parameter list and appear in the same scope are called overloaded functions.
// OVERLOADED functions
void func(const char *cp);
void func(const int a);
void func(const int *ip, const string str);
void func(const int i, const string &str);
- Overloaded functions must differ in the number or type(s) of the parameters.
- It's an ERROR for two functions to differ ONLY in their
return
types. If the name and parameter list of two functions match but thereturn
types differ then the second declaration is an ERROR.
void func(int i);
bool func(int j); // ERROR: only the return type is different
- A parameter that has a
top-level const
is indistinguishable from the one that does not.
bool func1(Phone);
bool func1(const Phone); // redeclares func1(Phone); top-level const are ignored
int func2(Phone*);
int func(Phone* const); // redeclares func2(Phone*); top-level const are ignored
// in these declarations, the second declaration declares the same function as first
-
We can overload functions based on whether the
paramater
is areference(or pointer)
toconst or non-const
(b'coz suchconst
are low-level consts) of a given type. -
Overloading & Scope : If we declare a name in an inner scope that name hides the name(s) declared in an outer scope. Names donot override across scopes.
-
Usually its a bad idea to declare function at
local scope
. -
In C++, name lookups happen before type checking.
-
Some functions have parameters that are given a particular value in most (but not all) calls. In such cases we can declare that common value as a default argument for the function.
-
inline
andconstexpr
:inline
andconstexpr
functions are defined in Header File.- Unlike other functions,
inline
andconstexpr
functions may be defined multiple times in a program but all definitions of a giveninline
/constexpr
function must match exactly.
-
assert
andNDEBUG
:- These are two preprocessor facilities used for Debugging code.
assert
macro is defined in<cassert>
header.- B'coz preprocessor names are managed by the preprocessor not the Compiler, we donot need to provide
using
declaration for the preprocessor variables. assert
macro is often used to check for conditions that cannot happen.- The behaviour of
assert
macro depends on the status of the preprocessor variable namedNDEBUG
. - In addition to using
assert
, we can write our own conditional debugging code usingNDEBUG
,#indef
and#endif
-
assert
is a preprocessor macro. A preprocessor macro is a preprocessor variable which acts somewhat like aninline
function.
#include <cassert>
assert(expression);
-
If the expression is
false
then theassert
writes a message and terminates the program, otherwise it does nothing. -
If
NDEBUG
is defined,assert
does nothing. By default,NDEBUG
is not defined and hence, by default,assert
does a run-time check. -
A constant integral value of
0
or the pointer literalnullptr
can be converted to any pointer type. -
A pointer to any non-
const
type can be converted tovoid *
-
A pointer to any type can be converted to
const void *
-
Small integral types always promote to
int
or larger integral types. -
Function pointer :
- A pointer that points to a
function
rather than anobject
- Like any other pointer a function-pointer points to a particular type.
- A function's type is determined by the
return
type and the types of its parameters; the function name is not part of its type.
- A pointer that points to a
// function compares two strings
bool compareStrings(const string &str1, const string &str2);
// here, function compareStrings has a type "bool (const string &, const string &)"
// "ptrFunc" is a pointer to a function with return type "bool" and that takes two "const string refernces" as parameteres
bool (*ptrFunc)(const string &, const string &);
// the paranthesis around the pointer is necesary otherwise:
// here "pf" is NOT a pointer to a function
bool *pf(const string &, const string &); // pf is a function that returns a "bool *" type
- When we use the name of the pointer as a value, the function is automatically converted to a pointer.
// we can assign the address of function "compareStrings" to the function-pointer "ptrFunc" as:
ptrFunc = compareStrings; // OK
ptrFunc = &compareStrings; // OK
- We can use a pointer-to-function to call the function to which the pointer points. There is no need to dereference the pointer:
bool b1 = ptrFunc("Rustom", "Potter"); // OK
bool b2 = (*ptrFunc)("Rustom", "Potter"); // OK. Equivalent to previous call
bool b3 = compareStrings("Rustom", "Potter"); // OK
bool b4 = *ptrFunc("Rustom", "Potter"); // ERROR
-
There is no conversion b/w pointers of one function type and pointers of another function type.
-
Just as with
arrays
we cannot define parameters offunction type
; but we can have a parameter that is a pointer to a function. -
Just as with array type, we can write a parameter that looks like a
function type
but it will be treated like a pointer. -
Just as with
arrays
, we cannot return a function type but we can return a pointer to a function. -
The Compiler will not automatically treat a function return type as the corresponding pointer type; so we must return a pointer type.
-
The fundamental ideas behind
class
are :- Data Abstraction :
- Encapsulation :
-
Data Abstraction is a programming (and design) technique that relies on the separation of interface and implementation.
-
Interface : It consists of
operations
that the users of theclass
can execute. -
Implementation : It includes
- class's data members,
- bodies of the functions that constitute the interface,
- and any other functions that are necessary for class's defintion but not intended for general use.
-
Encapsulation enforces the separation of class's
interface
andimplementation
.
A
class
that is encapsulated hides itsimplementation
; users can use theinterface
but cannot access theimplementation
.
-
Abstract Data Type : A
class
that usesdata abstraction
andencapsulation
. -
Member functions must be declared inside the class; but can be defined outside/inside the class body.
-
Non-member functions that are part of the interface are declared and defined outside the class.
-
The compiler processes
class
in two steps:- The member declarations are compiled first.
- Then, the member function bodies are processed.
-
The name of the member defined outside the class must include the name of the class of which it is a member.
-
We donot need to use the implicit
this
pointer to access the members of the object on which the member function is executing. However, we do need to usethis
to access the object as a whole. -
Ordinarily,
non-member functions
that are part of theinterface of the class
should be declared(but not defined) in the same header as theclass
itself. -
IO classes are types that cannot be copied; so we may only pass them by reference.
-
Copying a class's
object
copies that object'smembers
-
Constructors :
- Classes control object initialization by defining one or more special member function called
constructors
Constructors
initializes data members of the class object.- A
constructor
is run whenever an object of the class is created. - A constructor has same name as its
class
- Unlike other functions,
constructor
has noreturn type
. - A
class
can have multiple constructors. Like anyoverloaded functions
, theoverloaded constructors
must differ in the numer or types of the parameters. - A
constructor
may not be declared asconst
.
- Classes control object initialization by defining one or more special member function called
-
When we create a
const
object of aclass
type, the object does not assume its "const
ness" untill after theconstructor
completes the initialization. -
constructors
can write toconst
object during their construction. -
Default constructor
:Default constructor
is the one that takes no arguments.- Classes control default initialization by defining a special constructor known as
Default constructor
- If a class does not explicitly define any constructor, then the compiler will implicitly define a
default constructor
for it. - The
compiler-generated constructor
is known asSynthesized Default Constructor
-
For most classes, the
synthesized constructor
initializes each data member in the class as follows:- If there is an
in-class initializer
, use it to initialize the member; - Otherwise,
default-initialize
the member
- If there is an
-
The compiler generates a
default constructor
automatically only if we donot define anyconstructor
for the class -
If we define any
constructor
, then the compiler will not generate anydefault constructor
untill unless we define it ourselves. -
The objects of built-in or compound types that are defined inside a block have
undefined
values whendefault initialized
. Thus, classes that have members of built-in or compound types should either initialize those members inside the class or define their own default constructor. -
If a
class C1
has a memberX
that has a clasa typeclass C2
and classC2
doesnot have a defualt constructor then the compiler cannot initialize the memberX
(ifC1
also does not have a defualt initializer) -
Constructors should not override
in-class initializers
except to use a different initial value. If you can't usein-class initializers
then each constructor should explicitly initialize each member of built-in type. -
Access Control and Encapsulation:
- In
C++
we use access specifiers (public
,private
) to enforceEncapsulation
.
- In
-
Members defined after
public
specifier are accessible to all parts of the program. -
The
public
members define theinterface
of theclass
. -
Members defined after
private
specifier are accessible to the member functions of theclass
but are NOT accessible to the<code>
that uses theclass
. -
The
private
section Encapsulates the implementation. -
The
constructor
andmember functions
that are part of the interface follow thepublic
specifier. -
The data members and the functions that are part of the implementation follow the
private
specifier. -
Each
access specifier
specifies the access level to the succeeding members. -
The specified access specifier remains in effect untill the next access specifier or the end of the class body.
-
We can define a class type either by using
struct
class
-
If we use
struct
keyword, then the members defined before the first access specifier arepublic
. -
If we use
class
keyword, then the member defined before the first access specifier areprivate
.
// using 'struct' keyword to define a class
struct myClassA{
std::string m_string = "Rustom"; // public (by default due to 'struct' keyword)
private:
std::string m_str = "Potter"; // private (due to 'private' access specifier)
std::int m_int = 217; // private (due to 'private' access specifier)
public:
std::string new_str = "RustomPotter217"; // public (due to 'public' access specifier)
}
// using 'class' keyword to define a class
class myClassB{
std::string m_string = "Rustom"; // private (by default due to 'class' keyword)
private:
std::string m_str = "Potter"; // private (due to 'private' access specifier)
std::int m_int = 217; // private (due to 'private' access specifier)
public:
std::string new_string = "RustomPotter217"; // public (due to 'public' access specifier)
}
friend
keyword :- A class can allow another class or function to access its non-
public
member functions by making that class or function itsfriend
. - Friends are not members of the class and are not affected by the access specifiers
friend
declaration may appear only inside a class definition- Ordinarily its a good idea to group
friend
declaration at the beginning or end of the class definition - A class a make another class its
friend
. - A class can also declare another member function (must be defined previously) of another class as its
friend
. - A
friend
function can be defined inside the class body. - Each class controls which classes or functions are its friends.
- A
friend
of afriend
has no access to the class members. - When we declare a member function as a
friend
; we must specify theclass
of which that function is a member. - A
friend
declaration affects access, but it is not declaration in an ordinary sense.
- A class can allow another class or function to access its non-
class Sales_data{
// friend declaration for non-member Sales_data function
friend std::istream &read(std::istream&, Sales_data&)
//......
};
-
Although, user code need not change when the
class
definition changes, the source code that uses a class must be compiled everytime the class definition changes. -
In addition to data and function members, a
class
can define its own local names fortypes
-
Type names defined by a class are subject to the same access control as any other member. It can be either
public
orprivate
. -
Unlike ordinary members, members that define types must appear before they are used. As a result, type members usually appear at the beginning of the class.
-
Member functions defined inside the class are automatically
inline
. -
We can explicitly declare a member function as
inline
as part of its declaration inside the class body. -
We can also define a member function as
inline
by specifyinginline
on the function definition outside the class body. -
mutable
data members:- Data members that can be modified even inside a
const
member function. - A
mutable
data member is neverconst
even if it is a member of aconst
object. - A
const
member function may change the value of amutable
data member.
- Data members that can be modified even inside a
-
in-class
initializers must use:- = form of initialization or,
- direct form of initialization (using curly braces)
-
We access
type members
from the class using theScope operator (::)
-
Name Lookup : The process of finding which
declarations
match the use of a name. -
Ordinarily, an inner scope can redefine a name from an outer scope even if that name has already been used in the inner scope. However, in a class, if a member uses a name from an outer scope and that name is a
type
, then the class may not subsequently redefine that name
typedef double Money;
class Account{
public:
Money balance (){ return bal;}
private:
typedef double Money; // ERROR. cannot redefine Money
Money bal;
};
-
Although its an ERROR to redefine a
name type
, Compilers are not required to diagnose this error. -
Definitions of
type names
usually should appear in the beginning of theclass
. That way, any member that uses that type name will be seen after the type name has already been defined. -
Default Constructor
is used automatically whenever an object isDefault-
orValue-
initialized. -
Default initialization
happens:- When we define non-
static
variables orarrays
at block scope without initializers. - When a
class
that itself has members ofclass-type
uses the synthesized default constructor. - When members of
class-type
are not explicitly initialized in a constructor initializer list.
- When we define non-
-
Value Initialization
happens:- during
array
initialization, when we provide fewer initializers than the size of the array. - when we define a local
static
object without an initializer. - when we explicitly request value-initialization by writing an expression of the form
T()
whereT
is the name of atype
.
- during
-
Converting Constructor : A constructor that can be called with a single argument defines an implicit conversion from the constructor's parameter type to the class type.
-
The compiler will apply only one
type
conversion at a time. -
Class type conversions are not always useful, b'coz the class-type object created is temporary.
-
We can prevent the use of constructor in a context that requires and implicit conversion by declaring the constructor as
explicit
-
The
explicit
keyword is meaningful only on constructors that can be called with a single argument. -
Constructors that require more than one argument are not used to perform an implicit type conversion, so there is no requirement to convert those constructors as
explicit
. -
One context in which implicit conversions happen is when we use copy initialization.
-
We cannot use
explicit
constructors with copy initialization. We must use direct initialization -
When a constructor is declared
explicit
, it can only be used with the direct initialization -
The compiler will not use
explicit
constructors in automatic conversions. -
We can use
static_cast
to perform an explicit rather than implicit conversion. -
The
string
constructor that takes a single parameter of typeconst char*
is NOTexplicit
. -
The
vector
constructor that takes a size isexplicit
. -
An
aggregate class
give users direct access to its members and has special initialization syntax. -
A class is
aggregate
if:- all of its data members are
public
. - it does not define any constructors.
- it has no
in-class initializers
. - it has no
base class
orvirtual functions
.
- all of its data members are
-
The parameters and
return type
ofconstexpr
functions must beliteral
types. -
An
aggregate class
whose data members are all ofliteral
type is aliteral class
-
Destructor
: The class member that destroys objects of theclass type
.