C++ outline and interview questions (3): Class

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;


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值