3.1. Contents
3.1.1 The main features of C++.
(1) Encapsulation: access control.
(2) Inherence: some attributes (member variables) and/ormethods (non-static member functions) of a class (parent or base) can be usedby another class (child or derived).
(3) Polymorphism: a method behaves differently by thedifferent inputs.
3.1.2 Declaration and definition.
Class name /struct name / enum name does not conflict with a variable name.
Template namecannot be the same with a variable name.
3.1.3 Data alignment for class / struct.
3.1.4 Static members.
The key of static member isthat we cannot use this pointer tocall it.
(3.1) it is illegal to calla non-static member in a static member function.
(3.2) it is legal to call astatic member in a non-static member function. This is because a static memberis attached to the definition of a class, then will be shared be all objects ofthis type.
(3.3) a static membervariable should be explicitly (i.e.,it should be initialized outside of the scope of the definition of a class)initialized before using, since itcannot be initialized through ctor of the class. Vice versa, it need to be re-allocatedafter using.
3.1.5 Nested class andContainer class.
Class to hold objects inmemory or external storage. It acts as a generic holder.
- It has a predefined behavior and a knowninterface.
- It is used to hide the topology used formaintaining the list of objects in memory.
The container class can be of two types:
- Heterogeneous container – Here the containerclass contains a group of mixed objects
- Homogeneous container - Here the containercontains all the same objects.
3.1.6 scope: name look-up.
(5.1) From the outside of thedefn of a class: for the usual-defined functions, the range is determined basedon the type of object of the ref of a pointer; for the virtual functions, findthe proper entrance (function pointer) from the virtual table. (This is why we need to declare the dtor tobe virtual when we take advantage of the polymorphism property.)
(5.2) For the inside of thedefn of a class: Unless indicated by scope resolution operator, we call afunction in the current scope.
3.1.7 “this” pointer.
3.1.8 operator overloading.
(1) Arithmetic operators:Can be defined as member function or friend function. The latter one is better: The member functions takes only oneparameter which requires that the left-hand of the operator should be of theclass type.
(2) Conversion operator.
(3) operator new.
(4) operator [].
2.2. Questions
Q: What aredifferences between C and C++?
A: Three mainfeatures of class. Safer code: reference, smarter pointers and dynamic_cast.
Q: What is class?
A: A class is an extensibleprogram-code-template for creating objects, providing initial values for state(member variables) and implementations of behavior (member functions ormethods). A class is a description of a definition.
Note: A class will not take memory beforeit is actually instantiated. (It will be posed in the code segment.)
Q: What is encapsulation?
A: Containing and hiding Informationabout an object, such as internal data structures and code. Encapsulationisolates the internal complexity of an object's operation from the rest of theapplication. For example, a client component asking for net revenue from abusiness object need not know the data's origin.
Q: What is inherence?
A: See the contents.
Q: What is polymorphism?
A: See the contents.
Q: What is "this" pointer?
A: “this” pointer is a pointeraccessible only within the member functions of a class, struct, or union type.It points to the object for which the member function is called. Static memberfunctions do not have a “this” pointer. When a non-static member function iscalled for an object, the address of the object is passed as a hidden argumentto the function. For example, the following function calls
myDate.setMonth(3 );
canbe interpreted this way:
setMonth(&myDate, 3 );
Theobject's address is available from within the member function as the “this”pointer. It is legal, though unnecessary, to use the “this” pointer when referringto members of the class.
Q: What happens when you make call"delete this;"?
A: As long as you’re careful, it’s okay(not evil) for an object to commit suicide (delete this).
Here’show I define “careful”:
(1)You must be absolutely 100% positively sure that this object was allocated vianew (not by new[], nor by placement new, nor a local object on the stack, nor anamespace-scope / global, nor a member of another object; but by plain ordinarynew).
(2)You must be absolutely 100% positively sure that your member function will bethe last member function invoked on this object.
(3)You must be absolutely 100% positively sure that the rest of your memberfunction (after the delete this line) doesn’t touch any piece of this object(including calling any other member functions or touching any data members).This includes code that will run in destructors for any objects allocated onthe stack that are still alive.
(4)You must be absolutely 100% positively sure that no one even touches the thispointer itself after the delete this line. In other words, you must not examineit, compare it with another pointer, compare it with nullptr, print it, castit, do anything with it.
Naturallythe usual caveats apply in cases where your this pointer is a pointer to a baseclass when you don’t have a virtual destructor.
Q: What is assignment operator?
A: Default assignment operator handlesassigning one object to another of the same class.
Memberto member copy (shallow copy)
Q: What are all the implicit memberfunctions of the class? Or what are all the functions which compiler implementsfor us if we don't define one?
A: (a) default ctor (b) copy ctor (c)assignment operator (d) default destructor (e) address operator
Q: What is a container class? What are thetypes of container classes?
A: A container class is a class that isused to hold objects in memory or external storage. A container class acts as ageneric holder. A container class has a predefined behavior and a well-knowninterface. A container class is a supporting class whose purpose is to hide thetopology used for maintaining the list of objects in memory. When a containerclass contains a group of mixed objects, the container is called aheterogeneous container; when the container is holding a group of objects thatare all the same, the container is called a homogeneous container.
Q: What is a nested class? Why can it beuseful?
A: A nested class is a class defined enclosed within the scope ofanother class. Nested classes are useful for organizing code and controllingaccess and dependencies. Nested classes obey access rules just like other partsof a class do. When you instantiate as outer class, it won't instantiate insideclass.
Q: What is a local class? Why can it beuseful?
A: A local class is a class defined within the scope of a function. Likenested classes, local classes can be a useful tool for managing codedependencies.
Q: How do I create a subscript operator fora Matrix class?
A: Use operator() rather thanoperator[].
Whenyou have multiple subscripts, the cleanest way to do it is with operator()rather than with
operator[].The reason is that operator[] always takes exactly one parameter, butoperator() can
takeany number of parameters (in the case of a rectangular matrix, two parametersare needed).
For example:
classMatrix {
public:
Matrix(unsignedrows, unsigned cols);
double& operator() (unsigned row, unsigned col); //subscript operators often come inpairs
double operator() (unsigned row, unsigned col) const; //subscript operators often comein pairs
...
~Matrix();// Destructor
Matrix(constMatrix& m); // Copy constructor
Matrix& operator= (const Matrix& m); // Assignment operator
...
private:
unsigned rows_, cols_;
double *data_;
};
inline Matrix::Matrix(unsignedrows, unsigned cols) : rows_ (rows), cols_ (cols) //data_<--initialized below (after the 'if/throw' statement)
{
if(rows == 0 || cols == 0)
throwBadIndex("Matrix constructor has 0 size");
data_= new double[rows * cols];
}
inline Matrix::~Matrix()
{
delete[]data_;
}
inline double&Matrix::operator() (unsigned row, unsigned col)
{
if(row >= rows_ || col >= cols_)
throwBadIndex("Matrix subscript out of bounds");
return data_[cols_*row + col];
}
inline doubleMatrix::operator() (unsigned row, unsigned col) const
{
if(row >= rows_ || col >= cols_)
throwBadIndex("const Matrix subscript out of bounds");
return data_[cols_*row + col];
}
Then you can access an element of Matrix m using m(i,j) rather than m[i][j]:
int main()
{
Matrixm(10,10);
m(5,8)= 106.15;
std::cout<< m(5,8);
...
return 1;
}
Q: Why shouldn't my Matrix class'sinterface look like an array-of-array?
A: Here's what this FAQ is really allabout: Some people build a Matrix class that has an
operator[]that returns a reference to an Array object (or perhaps to a raw array,shudder), and
thatArray object has an operator[] that returns an element of the Matrix (e.g., areference to a
double).Thus they access elements of the matrix using syntax like m[i][j] rather thansyntax like
m(i,j).
Thearray-of-array solution obviously works, but it is less flexible than theoperator() approach.
Specifically,there are easy performance tuning tricks that can be done with the operator()
approachthat are more difficult in the [][] approach, and therefore the [][] approachis more
likelyto lead to bad performance, at least in some cases.
Forexample, the easiest way to implement the [][] approach is to use a physicallayout of the
matrixas a dense matrix that is stored in row-major form (or is it column-major; Ican't ever
remember).In contrast, the operator() approach totally hides the physical layout of thematrix,
andthat can lead to better performance in some cases.
Putit this way: the operator() approach is never worse than, and sometimes betterthan, the [][]
approach.
Theoperator() approach is never worse because it is easy to implement the dense,row-major
physicallayout using the operator() approach, so when that configuration happens to bethe
optimallayout from a performance standpoint, the operator() approach is just as easyas the [][]
approach(perhaps the operator() approach is a tiny bit easier, but I won't quibble overminor
nits).
Theoperator() approach is sometimes better because whenever the optimal layout fora given
applicationhappens to be something other than dense, row-major, the implementation isoften
significantlyeasier using the operator() approach compared to the [][] approach.
Asan example of when a physical layout makes a significant difference, a recentproject
happenedto access the matrix elements in columns (that is, the algorithm accesses allthe
elementsin one column, then the elements in another, etc.), and if the physical layoutis rowmajor,
theaccesses can "stride the cache". For example, if the rows happen tobe almost as big as
theprocessor'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 changingthe
mappingfrom the logical layout (row,column) to the physical layout (column,row).
Ofcourse there are many examples of this sort of thing from numerical methods,and sparse
matricesare a whole other dimension on this issue. Since it is, in general, easier toimplement a
sparsematrix or swap row/column ordering using the operator() approach, theoperator()
approachloses nothing and may gain something it has no down-side and a potentialup-side.
Usethe operator() approach.
Q: Should I design my classes from theoutside (interfaces first) or from the inside (data first)?
A:From the outside!
A good interface provides a simplified view that is expressed in the vocabularyof a user. In the case of OO software, the interface is normally the set ofpublic methods of either a single class or a tight group of classes. Firstthink about what the object logically represents, not how you intend tophysically build it. For example, suppose you have a Stack class that will bebuilt by containing a LinkedList:
classStack {
public:
...
private:
LinkedListlist_;
};
Shouldthe Stack have a get() method that returns the LinkedList? Or a set() methodthat takes a
LinkedList?Or a constructor that takes a LinkedList? Obviously the answer is No, since you
shoulddesign your interfaces from the outside-in. I.e., users of Stack objects don'tcare about
LinkedLists;they care about pushing and popping.
Nowfor another example that is a bit more subtle. Suppose class LinkedList isbuilt using a linked list of Node objects, where each Node object has a pointerto the next Node:
classNode { /*...*/ };
classLinkedList {
public:
...
private:
Node*first_;
};
Shouldthe LinkedList class have a get() method that will let users access the firstNode? Should
theNode object have a get() method that will let users follow that Node to thenext Node in the
chain?In other words, what should a LinkedList look like from the outside? Is aLinkedList
reallya chain of Node objects? Or is that just an implementation detail? And if it isjust an
implementationdetail, how will the LinkedList let users access each of the elements in the
LinkedListone at a time?
Thekey insight is the realization that a LinkedList is not a chain of Nodes. Thatmay be how it is
built,but that is not what it is. What it is is a sequence of elements. Therefore theLinkedList
abstractionshould provide a LinkedListIterator class as well, and that LinkedListIteratormight
havean operator++ to go to the next element, and it might have a get()/set() pairto access its
valuestored in the Node (the value in the Node element is solely the responsibilityof the
LinkedListuser, which is why there is a get()/set() pair that allows the user to freelymanipulate
thatvalue).
Startingfrom the user's perspective, we might want our LinkedList class to supportoperations
thatlook similar to accessing an array using pointer arithmetic:
voiduserCode(LinkedList& a)
{
for(LinkedListIterator p = a.begin(); p != a.end(); ++p)
std::cout<< *p << '\n';
}
Toimplement this interface, LinkedList will need a begin() method and an end()method. These
returna LinkedListIterator object. The LinkedListIterator will need a method to goforward, ++p;
amethod to access the current element, *p; and a comparison operator, p !=a.end().
Thecode follows. The important thing to notice is that LinkedList does not haveany methods
thatlet users access Nodes. Nodes are an implementation technique that iscompletely buried.
Thismakes the LinkedList class safer (no chance a user will mess up the invariantsand linkages
betweenthe various nodes), easier to use (users don't need to expend extra effortkeeping the
node-countequal to the actual number of nodes, or any other infrastructure stuff), andmore
flexible(by changing a single typedef, users could change their code from usingLinkedList to
someother list-like class and the bulk of their code would compile cleanly andhopefully with
improvedperformance characteristics).
#include // Poor man's exception handling
classLinkedListIterator;
classLinkedList;
classNode {
//No public members; this is a "private class"
friend class LinkedListIterator; // A friend class
friend class LinkedList;
Node *next_;
int elem_;
};
class LinkedListIterator {
public:
booloperator== (LinkedListIterator i) const;
booloperator!= (LinkedListIterator i) const;
voidoperator++ (); // Go to the next element
int& operator* (); // Access the current element
private:
LinkedListIterator(Node*p);
Node* p_;
friend class LinkedList; // so LinkedList can construct a LinkedListIterator
};
class LinkedList {
public:
void append(int elem); // Adds elem after the end
void prepend(int elem); // Adds elem before the beginning
...
LinkedList Iteratorbegin();
LinkedList Iteratorend();
...
private:
Node* first_;
};
Hereare the methods that are obviously inlinable (probably in the same headerfile):inlinebool LinkedListIterator::operator== (LinkedListIterator i) const
{
returnp_ == i.p_;
}
inlinebool LinkedListIterator::operator!= (LinkedListIterator i) const
{
return p_ != i.p_;
}
inlinevoid LinkedListIterator::operator++()
{
assert(p_!= NULL); // or if (p_==NULL) throw ...
p_ = p_->next_;
}
inlineint& LinkedListIterator::operator*()
{
assert(p_!= NULL); // or if (p_==NULL) throw ...
return p_->elem_;
}
inlineLinkedListIterator::LinkedListIterator(Node* p) : p_(p) { }
inlineLinkedListIterator LinkedList::begin()
{
return first_;
}
inlineLinkedListIterator LinkedList::end()
{
return NULL;
}
Conclusion:The linked list had two different kinds of data. The values of the elementsstored in
thelinked list are the responsibility of the user of the linked list (and only theuser; the linked list
itselfmakes no attempt to prohibit users from changing the third element to 5), andthe linked
list'sinfrastructure data (next pointers, etc.), whose values are the responsibilityof the linked list
(andonly the linked list; e.g., the linked list does not let users change (or evenlook at!) the
variousnext pointers).
Thusthe only get()/set() methods were to get and set the elements of the linkedlist, but not the
infrastructureof the linked list. Since the linked list hides the infrastructurepointers/etc., it is
ableto make very strong promises regarding that infrastructure (e.g., if it were adoubly linked
list,it might guarantee that every forward pointer was matched by a backwardspointer from the
nextNode).
So,we see here an example of where the values of some of a class's data is theresponsibility of
users(in which case the class needs to have get()/set() methods for that data) butthe data that the
classwants to control does not necessarily have get()/set() methods.
Note:the purpose of this example is not to show you how to write a linked-listclass. In fact you
shouldnot "roll your own" linked-list class since you should use one of the"container classes"
providedwith your compiler. Ideally you'll use one of the standard container classessuch as the
std::listtemplate.
Q: Is “non-const static member variables must be defined outsideof the class for them to be used” right?
A: Yes.
struct test
{
static int x;
};
int test::x;