Wednesday, September 21, 2016

C++ Review Points

constructor/deconstructor:

[review points]:
1) several type of constructors:
   default constructor
   parameterized constructor
   copy constructor
how are these constructors got called?
2) why virtual deconstructor? 

[code]
class Base{
    private:
        int member;
    public:
        Base(void) = default;
        Base(int v):member(v){};
        Base(const Base &b)
        {
           member = b.member;
        }
        Base &operator= (const Base &b)
        {
           member = b.member;
           return *this;
        }
        virtual ~Base(void){}
};

 inheritence:

[review points]:


type casts:

static_cast



static_cast is used for cases where you basically want to reverse an implicit conversion, with a few restrictions and additions. static_cast performs no runtime checks. This should be used if you know that you refer to an object of a specific type, and thus a check would be unnecessary. 



Example:

void func(void *data) {
  // Conversion from MyClass* -> void* is implicit
  MyClass *c = static_cast<MyClass*>(data);
  ...
}

int main() {
  MyClass c;
  start_thread(&func, &c)  // func(&c) will be called
      .join();
}

In this example, you know that you passed a MyClass object, and thus there isn't any need for a runtime check to ensure this.

dynamic_cast


dynamic_cast is used for cases where you don't know what the dynamic type of the object is. You cannot use dynamic_cast if you downcast and the argument type is not polymorphic. An example:

if(JumpStm *j = dynamic_cast<JumpStm*>(&stm)) {
  ...
} else if(ExprStm *e = dynamic_cast<ExprStm*>(&stm)) {
  ...
}

dynamic_cast returns a null pointer if the object referred to doesn't contain the type casted to as a base class (when you cast to a reference, a bad_cast exception is thrown in that case).

The following code is not valid, because Base is not polymorphic (it doesn't contain a virtual function):

struct Base { };
struct Derived : Base { };
int main() {
  Derived d; Base *b = &d;
  dynamic_cast<Derived*>(b); // Invalid
}

An "up-cast" is always valid with both static_cast and dynamic_cast, and also without any cast, as an "up-cast" is an implicit conversion.
Regular Cast

These casts are also called C-style cast. A C-style cast is basically identical to trying out a range of sequences of C++ casts, and taking the first C++ cast that works, without ever considering dynamic_cast. Needless to say, this is much more powerful as it combines all of const_cast, static_cast and reinterpret_cast, but it's also unsafe, because it does not use dynamic_cast.

In addition, C-style casts not only allow you to do this, but they also allow you to safely cast to a private base-class, while the "equivalent" static_cast sequence would give you a compile-time error for that.

Some people prefer C-style casts because of their brevity. I use them for numeric casts only, and use the appropriate C++ casts when user defined types are involved, as they provide stricter checking.

const cast


This one is primarily used to add or remove the const modifier of a variable.

const int myConst = 5;
int *nonConst = const_cast<int*>(&myConst); // removes const

Although const cast allows the value of a constant to be changed, doing so is still invalid code that may cause a run-time error. This could occur for example if the constant was located in a section of read-only memory.

*nonConst = 10; // potential run-time error

Const cast is instead used mainly when there is a function that takes a non-constant pointer argument, even though it does not modify the pointee.

void print(int *p) 
{
   std::cout << *p;
}

The function can then be passed a constant variable by using a const cast.

print(&myConst); // error: cannot convert 
                 // const int* to int*

print(nonConst); // allowed

regular cast

These casts are also called C-style cast. A C-style cast is basically identical to trying out a range of sequences of C++ casts, and taking the first C++ cast that works, without ever considering dynamic_cast. Needless to say, this is much more powerful as it combines all of const_cast, static_cast and reinterpret_cast, but it's also unsafe, because it does not use dynamic_cast.In addition, C-style casts not only allow you to do this, but they also allow you to safely cast to a private base-class, while the "equivalent" static_cast sequence would give you a compile-time error for that.Some people prefer C-style casts because of their brevity. I use them for numeric casts only, and use the appropriate C++ casts when user defined types are involved, as they provide stricter checking.

operator overload:

class Complex {
private:
int _a, _b;
public:
Complex(int a=0, int b=0):_a(a),_b(b){};

// 1. over loading syntax

const Complex operator + (const Complex &c) 
{
return Complex(_a+c._a, _b+c._b);
}

Complex & operator += (const Complex &c)

{
_a += c._a; 
       _b += c._b;
return *this; 
}

// 2. unary operator overloading


const Complex operator -() const

{
return Complex(-_a, -_b);


const Complex operator ++() 

{
return Complex(++_a, ++_b);
}
const Complex operator ++(int)
{
return Complex(_a++, _b++);


const Complex operator --() 

{
return Complex(--_a, --_b);
}
const Complex operator --(int)
{
return Complex(_a--, _b--);


// 3. binary operator overloading

friend const Complex operator +( const Complex &left, const Complex &right)
{
return Complex(left._a + right._a, left._b + right._b);
}

// 4. iostream overloading

friend ostream & operator << ( ostream &os, const Complex &c)
{
os << "Complex:" << c._a <<"+" << c._b << endl;
return os;
}

#if 0
friend istream &operator >> ( istream &is, Complex &c)
{
     is >> c._a >> c._b;
     return is;
}
#endif

friend istream &operator >> (istream &is, Complex &c)

{
    string mystr;
    cout << "first ele";
    
    getline(is, mystr);
    stringstream(mystr) >> c._a;
    cout << "second ele";
    getline(is, mystr);
    stringstream(mystr) >> c._b;
    return is;
}
};

functor

class Fctor {
    private:
         int m;
    public:
         Fctor(int x):m(x){};
         int operator() (int y) { return m + y; }
};


int main(void)

{
     Fctor a(10);
     cout << a(20) << endl;
     return 0;
}

smart pointer:


class TBase {
public:
TBase(void){ cout << "TBase ctor" << endl; }
        void display() { cout << "Display" << endl; }
~TBase(void) { cout << "TBase dtor" << endl; }
};


void rawfunction(void)
{
       TBase *tb = new TBase;
}

void scopedfunction(void)
{
unique_ptr<TBase> ptr (new TBase);
        ptr -> display();
}

shared_ptr<TBase> sharedfunction(void)
{
shared_ptr<TBase>ptr (new TBase);
        shared_ptr<TBase> ptr1 = ptr;
ptr ->display();
        return ptr1;
}

void autofunction(void)
{
auto_ptr<TBase>ptr(new TBase);
ptr->display();
}

void weakfunction(void)
{
        shared_ptr<TBase>sptr ( new TBase);
    weak_ptr<TBase>ptr = sptr;
//        cout << "weaked_ptr: is" << ptr << endl;
sptr->display();
}

void uniquefunction(void)
{
     unique_ptr <TBase> uptr ( new TBase);
     unique_ptr <TBase> wptr;
     wptr = move(uptr);
     wptr->display();
}



int main(void)
{
    shared_ptr<TBase>ptr;

    rawfunction();
    cout <<"XXXXX" << endl;
    scopedfunction();

    cout <<"XXXXX" << endl;
    ptr= sharedfunction();

    cout <<"XXXXX" << endl;
    autofunction();

    cout <<"XXXXX" << endl;
    weakfunction();

    cout <<"XXXXX" << endl;
    uniquefunction();
    return 1;
}


template/stl


sizeof



reference


I/O



binding (c++11)



exceptions


RAII


design patterns


No comments:

Post a Comment