C++ Review - More On Functions, Variables, Classes
C++ Review - More On Functions, Variables, Classes
1
Key Concept #1:
Global vs. Member Functions
"it's a good practice adding 'static' in front of functions that you're sure only you would use it"
2
Key Concept #3: Function Prototype
vs. Function Definition
u Think, which one is better?
1. void f() {
…
}
int main() {
…
f();
}
2. void f(); ç function prototype
int main() {
…
f();
}
void f() {
… ç function definition
}
Data Structure and Programming Prof. Chung-Yang (Ric) Huang 5
3
Key Concept #5:
Parameters in a function
u When a function is called, the caller
performs “=“ operations on its arguments to
the corresponding parameters in the
function
l void f(int a, char c, int *p) { ... }
...
int main() {
f(i, cc, pp); // int a = i;
// char c = cc;
// int *p = pp;
}
4
Passed by Object, Pointer, and Reference
[Rule of thumb] Making an ‘=’ (i.e. copy) from the passed
argument in the caller, to the parameter of the called function.
void f1(int a) main()
{ a = 20; } {
void f2(int& a) int a = 10;
{ a = 30; } int* p = &a;
void f3(int* p) int a1,a2,a3,a4,a5;
{ *p = 40; } f1(a); a1 = a;
void f4(int* p) f2(a); a2 = a;
{ p = new int(50); } f3(p); a3 = *p;
void f5(int* & p) f4(p); a4 = *p;
{ p = new int(60); } f5(p); a5 = *p;
}
a1 = 10;
What are the values of a1, a2, a3, a4, and a5 at the end? a2 = 30;
a3 = 40;
Data Structure and Programming Prof. Chung-Yang (Ric) Huang 10 a4 = 40;
a5 = 60;
Summary #1:
Passed by pointer or passed by reference
1. If you have some data to share among functions,
and you don’t want to copy (by ‘=‘) them during
function calling, you can use “passed by pointers”
class A {
int _i; char _c; int *_p; ...
};
void f(A *a) { ... }
...
int main() {
A *a = ...;
f(a);
}
5
Summary #1:
Passed by pointer or passed by reference
2. However, if originally the data is not a pointer
type, “passed by pointers” is kind of awkward.
You should use “passed by references”
class A {
int _i; char _c; int *_p; ...
};
void f(A *a) { ... }
void g(A& a) { ... }
...
int main() {
A a = ...; // an object, not a pointer
f(&a); // Awkward!! C style L
g(a); // Better!!
}
Summary #1:
Passed by pointer or passed by reference
3. But, sometimes we just want to share the
data to another function, but don’t want it to
modify the data.
int main() {
A a = ...;
g(a);
}
void g(A& a) { ... }
// “a” may get modified by g()
è Using “const” to constrain !!
6
Part II: More on “Variables”
u “const” keyword
u Pointer arithmetic
u Compilation issues
u Compiler preprocessors
Data Structure and Programming Prof. Chung-Yang (Ric) Huang 14
7
What? const *& #$&@%#q
u Rule of thumb
l Read from right to left
1. f(int* p)
l Pointer to an int (integer pointer)
2. f(int*& p)
l Reference to an integer pointer
3. f(int*const p)
l Constant pointer to an integer
4. f(const int* p) = f(int const * p)
l Pointer to a constant integer
5. f(const int*& p)
l Reference to a pointer of a constant int
6. f(const int*const& p)
l Reference to a constant pointer address, which points
to a constant integer
8
Key concept #2: The Impact of const
u Supposed “_data” is a data member of class MyClass
void MyClass::f() const
{
_data->g();
}
l Because this object is treated as a constant, its data field
“_data” is also treated as a constant in this function
è “g()” must be a constant method too!!
l Compiler will signal out an error if g() is NOT a const method
9
Const vs. non-const??
u Passing a const argument to a non-const
parameter in a function
void f(A& i) { ... }
void g(A j) { ... }
int main() {
const A a(...);
f(a); // Error à No backdoor for const
g(a); // a copy of “a” is treated non-const in g()
}
10
Casting “const” to “non-const”
const T a;
a.nonConstMethod(); // not OK
Trying...
1. T(a).nonConstMethod();
l Static cast; OK, but may not be safe (why?)
explicitly calling copy constructor, so it's the newly build
l Who is calling nonConstMethod()? instance of the class calling nonConstMethod(), so
2. const_cast<T>(a).nonConstMethod(); compile okay.
l Compilation error!!
l “const_cast” can only be used for pointer,
reference, or a pointer-to-data-member type
3. const_cast<T *>(&a)->nonConstMethod();
l OK, but kind of awkward
const T* p;
p->nonConstMethod(); // not OK
è const_cast<T*>(p)->nonConstMethod();
A const object can now call non-const method
11
const class object (revisited)
u Remember:
const A& B::blah (const C& c) const {...}
l When an object of class B calls this
member function, this object will become a
“const class object”.
l That is, the B’s data members will be
treated as const (i.e. can’t be modified) in
this function.
l Also, “this” cannot call non-const functions
in “blah()”, nor can the data members call
non-const functions.
{
_flags |= 0x1; // setting a bit of the _flags
}
l In such case, declare “_flag” with “mutable”
keyword often used in graph traversals, such
that it could be modified even in const
§ e.g. methods.
12
Key Concept #4: Array vs. Pointer
u An array variable represents a “const pointer”
l int a[10]; ç treating “a” as an “int * const”
a = anotherArr; // Error; can’t reassign “a”
l int *p = new int[10];
p = anotherPointer; // Compile OK, but memory leak!
p = new int(20); // also compile OK, but memory leak!
u An array variable (the const pointer) must be initialized
l Recall: “const” variable must be initialized
l Key: the size of the array must be known in declaration
l int a[10]; // OK, as the memory address is assigned.
int a[10] = { 0 }; // Initialize array variable and its content
int a[ ]; // NOT OK; array size unknown
int a[ ] = { 1, 2, 3 }; // OK array size determined by RHS
13
More about int [] and int*
u int a[10] = { 0 }; // type of a: “int *const”
int *p = new int[10];
*a = 10;
*p = 20; // OK
*(a + 1) = 20;
*(a++) = 30; // Compile error; explained later
a = p; // Compile error; non-const to const
p = a; // OK, but memory leak...
*(p++) = 40; // OK, but what about “delete [] p”?
int *q = a;
q[2] = 20;
*(q+3) = 30;
*(q++) = 40; // OK
delete a; // compile error/warning; runtime crash...
delete []p; // compile OK, but runtime crash (p = a)
delete []q; // compile OK, but may get fishy result
u What about:
int a = 10; int *p = &a; ... delete p;
14
(Recapped) Key Concept #6: Memory Sizes
15
Key Concept #8: Memory Alignment
u What are the addresses of these variables?
int *p = new int(10); // let addr(p) = 0x7fffe84ff0e0
char c = 'a';
int i = 20;
int *pp = new int(30);
char cc = 'b';
int *ppp = pp;
int ii = 40;
char ccc = 'c';
char cccc = 'd';
int iii = 30;
è Given a variable of predefined type with memory
size S (Bytes), its address must be aligned to a
multiple of S
Data Structure and Programming Prof. Chung-Yang (Ric) Huang 34
16
Return by Object, Pointer, and Reference
2. Return by pointer
l MyClass* f(...) { MyClass* p;...; return p; }
MyClass* q = f(...);
// Should we “delete q” later?
3. Return by reference (reference to whom?)
l MyClass& f(...) {...; return r; }
// r cannot be local (why?)
MyClass& s = f(...); // <------------|
MyClass t = f(...); // What’s the diff?
// Is it OK?
l [NOTE] Should NOT return the reference of a
local variable
è int& f() { int a; ...; return a; }
è compilation warning need to check it's valid;
often used with "return (*this)" such that
l MyClass& MyClass::f(...) we could easily concatenate operator and
{...; return (*this); } functions.
MyClass s;
MyClass& t = s.f(...); // <------------|
MyClass v = s.f(...); // What’s the diff?
17
Remember in a software project...
u Your program may have many classes...
u You should create multiple files for different
class definitions ---
l .h (header) files
è class declaration/definition, function
prototype
l .cpp (source) files
è class and function implementation
l Makefiles
è scripts to build the project
18
Key Concept #11: “#include”
u A compiler preprocessor
l Process before compilation
l Perform copy-and-paste
u This is NOT OK
l // no #include “b.h”
class A {
B _b;
};
u This is OK
l // no #include “b.h”
class B; // forward declaration
class A {
B *_b;
};
è The rule of thumb is “need to know the size of the class”!!
19
Key Concept #13:
Undefined or Redefined Issues
an object file only contains information u Undefined errors for variable/class/type/function
in which .cpp or .cc it's compiled from. l The following will cause errors in compiling a source file ---
so there exist a "jump place holder" for
int i = j; // If j is not declared before this point
the object file to find the actual
function, i.e. linking. A a; // If class A is not defined before this point
A *a; // If class A is not declared before this point
goo(); // If no function prototype for goo() before this point
l The following is OK when compiling each source file, but will
cause error during linking if goo() is NOT defined in any other
source file --
int goo(); // forward declaration
...
int b = goo();
u Redefined errors
l Variable/class/function is defined in multiple places
l May be due to multiple inclusions of a header file
20
Key Concept #14: “extern” in C++
u Remember, static variables and functions can only be
seen in the file scope è cannot be seen in other file
22
Using namespace
1. void g() {
MyNameSpace::a = 10;
} // “::” is the scope operator
2. using MyNameSpace::a;
void g() {
a = 10;
}
23
Summary #2: Declare, Define, & Use
u If something is declared, but not defined or used,
that is fine. (Compilation warning)
u If something is used before it is defined or
declared è compile (undefined) error.
u If something is defined in other file, you can use it
only if you forward declare it in this file. BUT you
cannot define it again in this file è compile
(redefined) error.
l Variable è “extern”
l Function è prototype, with or without “extern”
u If something is declared, but not defined, in this
file, you can use it and the compilation is OK. BUT
if it is not defined in any other file è linking
(undefined) error.
24
“#define” for an Identifier
1. To avoid repeated definition of a header file in multiple
C/C++ inclusions
l #ifndef MY_HEADER_H
#define MY_HEADER_H
// header file body...
// ... command line define something in gcc/g++:
#endif -D<some flags>
25
“#define” for a MACRO function
u #define <identifier>(<argList>) [tokenString]
l e.g.
#define MAX(a, b) ((a > b)? a: b)
// Why not “((a > b)? a: b)” ?
l e.g.
// Syntax error below!! Why??
#define MAX(int a, int b) ((a > b)? a: b)
u Disadvantage
l “#define” MACRO function is difficult to debug!!
è Cannot step in the definition (Why??)
l Use inline function (i.e. inline int max(int a, int b)) instead
u Bit-slicing
u Class wrapper
u “static” keyword
26
Key Concept #1: “struct” in C++
u [Note] “struct” is a C construct used for
“record type” data
l Very similar to “class” in C++, but in C,
there is no private/public, nor member
function, etc.
u However, “struct” in C++ inherits all the
in c++, struct is only a class
features of the “class” construct with all methods and data
l Can have private/public, member functions,
members set to "public";
27
Example of “union” often used to save some memory.
union U int
{ main()
private: {
int _a; U u;
char _b; u.setB('a');
public: cout << u.getA()
U() { _a = 0; } << endl;
int getA() const return 0;
{ return _a; } }
void setA(int i)
{ _a = i; }
char getB() const u What is the output???
{ return _b; } int('a'); 97.
void setB(char c)
{ _b = c; }
};
Anonymous union
u Union can be declared anonymously
l i.e. Omit the type specifier
u main() class A {
{ union T {
union { int _a;
int _a; double _b;
char _b; };
}; T _t;
int i = _a; void f() {
char j = _b; if (_t._a >
} 10)...
è used as non-union variables } it's my (I'm A) data field, why am I still using operator "." to
access it?
è What if it is NOT anonymous? }; anonymous union is more easy and institutive to use.
28
Key Concept #3: Another ways to save
memory: memory alignment and bit slicing
u Note: in 64-bit machine, data are 8-byte aligned
What are “sizeof(A)” below ? this is true for both pointers and classes,
that is, an instance of a class is also
l class A { char _a; }; 8-byte aligned.
l class A { int _i; bool _j; int* _k; }
l class A { int _i; bool _j; int* _k; char _l; }
u Recommendation
l Pack the data in groups of “sizeof(void*)”, or ---
l Use bit-slicing to save memory
class A {
int _id: 30;
int _gender: 1;
int _isMember: 1;
void f() { if (_isMember) _id += ...; }
};
29
A Closer Look at the Previous Example
class BddNode { // wrapper class for BddNodeInt
private:
size_t _nodeV;
};
class BddNodeInt { // as pointer variables
…
};
◆ Important concepts:
● No extra memory usage when wrapping a pointer variable with a
class
● However, you gain the advantages in using
constructor/destructor, operator overloading, etc, which are not
applicable for pointer type variables.
è BddNode a, b, c;… ; c = a & b;
● The LSBs can be used as flags or stored other information.
30
Key Concept #4: Enum
u A user-defined type consisting of a set of named
constants called enumerators
l e.g.
class T {
enum COLOR {
RED, // value = 0
BLUE, // value = 1
GREED = 5,
YELLOW // value = 6
};
};
u By default, first enumerator’s value = 0
u Each successive enumerator is one larger than the
value of the previous one, unless explicitly specified
(using “=“) with a value
Data Structure and Programming Prof. Chung-Yang (Ric) Huang 67
Scope of “enum”
u Enumerators are only valid within the scope it is
defined
l e.g.
class T {
enum COLOR { RED, BLUE };
};
è RED/BLUE is only seen within T
l To access enumerator outside of the class, use
explicit class name qualification
§ e.g. void f() { int i = T::RED; }
è But in this case, the enum must be defined as
public
Data Structure and Programming Prof. Chung-Yang (Ric) Huang 68
31
Common usage of “enum”
1. Used in function return type
meaningful variables make code more readable.
l Color getSignal() { ... }
Bitwise Masks
u To manipulate multiple control “flags” in a single integer
u enum ErrState {
NO_ERROR = 0,
DIV_ZERO = 0x1, // 001
OVERFLOAT = 0x2, // 010
INTERRUPT = 0x4, // 100
BAD_STATUS= DIV_ZERO | OVERFLOAT |
INTERRUPT
};
int ErrState status = NO_ERROR; // This line is OK
// To set the error status
status |= OVERFLOAT;
// To unset the error status
status &= ~DIV_ZERO;
// To test the error status
if ((status & INTERRUPT) != 0)
... we have no operator "|=" and "&="
also, enum is not
overload-able with any
è Compilation error... WHY??? defined yet @@ operator.
Data Structure and Programming Prof. Chung-Yang (Ric) Huang 70
32
Key Concept #5: “#define” vs. “enum”
1. #define RED 0
#define BLUE 1
#define GREEN 5
2. enum COLOR {
RED, // value = 0
BLUE, // value = 1
GREED = 5
};
u What’s the difference in terms of debugging?
l Using “#define” è Can only display “values”
l Using “enum” è Can display “names”
Recommendation: using “enum”
33
Key Concept #6: Class Wrapper
1. To create a “record” type with a cleaner
interface
l e.g. When passing too many parameters to
a function, creating a class to wrap them
up.
è Making sure data integrity (checked in
constructor)
è Creating member functions to enact
assumptions, constraints, etc.
34
Object-Wrapped Pointer Variables
If your program contains pointer-pointed memory that
is highly shared among different variables
uKeep the reference count
uPointer à internal class (e.g. class NodeInt)
Object à user interface (e.g. class Node)
class NodeInt { // a private class
friend class Node;
Data _data; Data _data
size_t _refCnt
Node _left;
Node _right; _left _right
size_t _refCnt; *_node
};
Data _data Data _data
class Node { size_t _refCnt size_t _refCnt
NodeInt *_node;
};
*_node *_node
Data Structure and Programming Prof. Chung-Yang (Ric) Huang 75
35
Key Concept #6: Class Wrapper
3. To keep track of certain data/flag changes and
handle complicated exiting/exception conditions
void f() {
x1.doSomething();
if (...) x2.doSomething();
else { x1.undo(); return; }
...
x2.undo(); x1.undo();
}
èVery easy to miss some actions...
void f() {
XKeeper xkeeper; // keep a list in xkeeper
xkeeper.doSomething(x1);
if (...) xkeeper.doSomething(x2);
else return;
} // ~XKeeper() will be called
Data Structure and Programming Prof. Chung-Yang (Ric) Huang 77
However,
u The keyword “static” is kind of overloaded in C++
1. Static variable in a file
2. Static variable in a function
3. Static function
4. Static data member of a class
5. Static member function of a class
36
So, what does “static” mean anyway?
u “static” here,
refers to “memory allocation” (storage class)
l The memory of “static xxx” is allocated
before the program starts (i.e. in fixed
memory), and stays unchanged throughout
the program
[cf] “auto” storage class
§ Memory allocated is controlled by the execution
process (e.g. local variables in the stack memory)
37
[Note] Storage class vs. visible scope
u Remember, “static” refers to static “memory
allocation” (storage class)
l We’re NOT talking about the “scope” of a variable
u The scope of a variable is determined by where
and how it is declared
l File scope (global variable)
l Block scope (local variable)
è However, the “static” keyword does constrains the
maximum visible scope of a variable or function to
be the file it is defined
class T
{
static int _count;
public:
T() { _count++; }
~T() { _count--; }
};
------------------------------------------------
int T::_count=0;
// Static data member must be initialized in some
// cpp file ==> NOT by constructor!!! (why?)
Data Structure and Programming Prof. Chung-Yang (Ric) Huang 82
38
Key Concept #10:
“static” Member Function in a Class
u Useful when you want to access the “static” data member but do
not have a class object (when there's no explicit instance exist, we could still modify the static data field of the
class)
l Calling static member function without an object
§ e.g. T::setGlobalRef();
l No implicit “this” argument (no corresponding object)
l Can only see and use “static” data members , enum, or
nested types in this class
§ Cannot access other non-static data members
u Usage
l T::staticFunction(); // OK
l object.staticFunction(); // OK
l T::staticFunction() { ... staticMember... } // OK
l T::staticFunction() { ... this... } // Not OK
l T::staticFunction() { ... nonStaticMember... } // Not OK
l T::nonstaticFunction() { ... staticMember... } // OK
39
Key Concept #11:
static_cast<T>(a)... Cast away static?? L
u Convert object “a” to the type “T”
l No consistency check (i.e. sizeof(T))
è static implies “compile time”
è May not be safe
è cf. dynamic_cast<T>(a)
l (Common use) // more safer use
// Parent-class pointer object wants to
// call the child-only method
class Child : public Dad { ... };
----------------------------------
void f()
{
Dad* p = new Child;
...
static_cast<Child *>(p)->childOnlyMethod();
};
40