Sei sulla pagina 1di 31

The assignment operation also evaluates to the RHS of the assignment operator.

The
expression (a=1) assigns 1 to a and also returns 1. So, the if condition is successful and
value of a is printed as 1.

int a=3, b=5, c=1;


int x=8;
x = a < (b < c); The expression (b < c) is evaluated first. It is equivalent to (5 < 1) and
this expression will return 0. So, the next part of expression will become a < 0 which is
equivalent to 3 < 0 and hence result is 0. This result is assigned to x and hence value of x
becomes 0.

unsigned int i = 5;
while (--i >= 0)
{
cout << "Hello World\n";
} Being an unsigned number, value of i cannot be less than 0. So, the statement i >= 0
will be always true. Hence, this program goes into an infinite loop. When i becomes 0
and decremented further, it wraps around and becomes MAXINT.

Dynamic memory allocation :


int* ip = new int;
*ip = i;

The only function of pre-processor is to expand macros. They can be thought of as


intelligent text processors. Only compilers have the capability to perform syntax
checking.

Function pointers always point to the starting address of a function.

--------------------------------
Smart Pointers: Reduce bugs caused by the misuse of pointers while retaining
efficiency. Like,
RAAI capability over raw ptrs.
Automatic garbage collection
Bounds checking.
a _ptr<T> is not a T *
disadv: Can’t be used with pointers created with new[].
1. Auto Ptr : Semantics of strict ownership, meaning that the auto_ptr instance is
the sole entity responsible for the object's lifetime. If an auto_ptr is copied, the
source loses the reference.
An auto_ptr containing an STL container may be used to prevent further
modification of the container
Even when implicitly copying auto_ptr objects -- for instance, if you make a
function call and pass an auto_ptr object, when the function returns, the contents
of auto_ptr will be changed to NULL
// Example 1
T* pt1 = new T;
// right now, we own the allocated object

// pass ownership to an auto_ptr


auto_ptr<T> pt2( pt1 );

// use the auto_ptr the same way


// we'd use a simple pointer
*pt2 = 12; // same as "*pt1 = 12;"
pt2->SomeFunc(); // same as "pt1->SomeFunc();"

// use get() to see the pointer value


assert( pt1 == pt2.get() );

// use release() to take back ownership


T* pt3 = pt2.release();

// delete the object ourselves, since now


// no auto_ptr owns it any more
delete pt3;

} // pt2 doesn't own any pointer, and so won't


// try to delete it... OK, no double delete

// Example 2: Using reset()


//
void h()
{
auto_ptr<T> pt( new T(1) );

pt.reset( new T(2) );


// deletes the first T that was
// allocated with "new T(1)"

} // finally, pt goes out of scope and


// the second T is also deleted

2. Shared/RCSP: Technique of storing the number of references, pointers, or


handles to a resource such as an object or block of memory. It is typically used as
a means of deallocating objects which are no longer referenced.
Disadv:
1. The frequent updates it involves are a source of inefficiency.
2. Can't handle reference cycles, an object which refers directly or indirectly to
itself. A mechanism relying purely on reference counts will never consider
cyclic chains of objects for deletion, since their reference count is guaranteed to
stay nonzero. Dealt using,
Weak reference is a reference that does not protect the referent object from
collection by a garbage collector. An object referenced only by weak references
is considered unreachable (or "weakly reachable") and so may be collected at
any time. Weak references are used to avoid keeping in memory referenced but
unneeded objects

Cyclic reference:

Ex:
struct CDad;
struct CChild;

typedef boost::shared_ptr<CDad> CDadPtr;


typedef boost::shared_ptr<CChild> CChildPtr;

struct CDad : public CSample


{
CChildPtr myBoy;
};

struct CChild : public CSample


{
CDadPtr myDad;
};

// a "thing" that holds a smart pointer to another "thing":

CDadPtr parent(new CDadPtr);


CChildPtr child(new CChildPtr);

// deliberately create a circular reference:

parent->myBoy = child;
child->myDad = dad;

// resetting one ptr...

child.reset();

parent still references the CDad object, which itself references the CChild.If we
now call dad.reset(), we lose all "contact" with the two objects. But this leaves
both with exactly one reference, and the shared pointers see no reason to delete
either of them! We have no access to them anymore, but they mutually keep
themselves "alive". This is a memory leak at best; in the worst case, the objects
hold even more critical resources that are not released correctly.

--------------------------------------------

Volatile keyword : pushes compiler not to use optimization

** Opaque ptr/PIMPL/Chesire Cat/Compiler Firewall:


Just trhe opaque handle-to-class/struct to,
1. enhance encapsulation
2. reduce compilation dependency/time

** COW semantics ex : in string class

string s("Hello");
string t = s; // t and s point to the same buffer of characters

t += " there!"; // a new buffer is allocated for t before


// appending " there!", so s is unchanged.

Stack is used for storing local variables as well as for passing parameters. Calling
function pushes the arguments into stack before control transfers to the called function. In
addition, return address and values of registers also get pushed on to the stack.

This is because we cannot assign const <type>* to <type>*.

Arrays with new can’t be initialized. No way to specify initializer.


Int* ip = new int[12]; -- where ?

Assume that we have a class MyClass that does not have a default constructor, but which
has defined a constructor which takes an int as a parameter. This means that the compiler
is not going to provide a default constructor for the class. When such a condition is true,
the following statements hold good for MyClass

1. Every constructor for every class that has a MyClass member must explicitly initialize
the MyClass member by passing an initial int value to the MyClass constructor.
2. The compiler will not synthesize the default constructor for classes that have members
of type MyClass. If such classes want to provide a default, they must define one
explicitly, and that constructor must explicitly initialize their MyClass member.
3. The MyClass type may not be used as the element type for a dynamically allocated
array.
4. Statically allocated arrays of type MyClass must provide an explicit initializer for each
element.
5. If we have a container such as vector that holds MyClass objects, we cannot use the
constructor that takes a size without also supplying an element initializer.
Hence in our case the program does not compile because of the point 3. above

Const and Ref members can be initialized only using the construction initialization list
.e.g Sample(int nCount =
Because C defaults to external linkage for consts, this makes sense.
C++ defaults to internal linkage for const
C++ also defines a special rule for initializing const static members of an integral
type. You may initialize such members inside the class body.

Member Functions default to global linkage.Can be called with object pointer(even


uninit).But this is absurd usage and might crash if,for ex, it accesses a member
variable.Use Static functions for this.

Unlike ordinary data members, static members are not initialized through the class
constructor(s) and instead should be initialized when they are defined.

Name Hiding: Happens irrespective of same/different method signature in derived class.


So holds for virtual/static everything.If one name is there in derived it will hide every
other func of that name in base.

Functions have class scope by default.But non-static ones need some handle of sort
atleast (to be called) that is why static functions are the choice.

2): m_ciMax(255){

..................................

operator int(){
cout << "In the overloaded int() operator" << endl;

return m_nIVal;
}

cls2 = 4 * cls;
cout << cls << endl;
This program works fine. The statement cls2 = 4 * cls first converts cls to an integer
using the int() operator and then performs multiplication. It then creates a temporary
object with the result of the multiplication.
After this this temporary object is used to assign cls2 thereby invoking the overloaded=
operator. In the cout statement the int() operator is used to extract the integer from cls2
and then the resulting integer is printed using the cout object.

** Deal mixed types by :


1. Friend func
2. conversion func
..................
Provide a overloaded global << operator. An example is as shown
ostream& operator << (ostream& os, const Test& rhs){
os << rhs.m_nMember << ", " + rhs.m_strName;
return os;
}
However this has a limitation. The overloaded function cannot access the private
member variables of the object. Make the overloaded operator a friend of Test if private
member variables are also to be displayed

void printArr(char (&pArr)[5])


Remember that printArr(char *), printArr(char []) and printArr(char [10]) all mean the
one and the same. They all pass the array as a pointer. Remember that the size of the
array is not passed to the called function in any way implicitly. Hence the called function
has no way of determining the size of the input array, just by looking at the pointer value.
However declarations like b forces compile time checking to happen in case printArr is
called for an array with size not equal to 5.

d)void swap(char* &pS, char* &pD)

**Virtual-functions wid Default parms:


1. When a virtual is called through a reference or pointer to base, then the default
argument is the value specified in the declaration of the virtual in the base class.
This is the reason why pa->SetPublicString prints In B->Public A
2. If a virtual is called through a pointer or reference to derived, the default argument is
the one declared in the version in the derived class.
This is the reason why pb->SetPublicString prints In B->Public B
3. If a call omits an argument that has a default value, then the value that is used is
the one defined by the type through which the function is called, irrespective of the
object's dynamic type
This is the reason why static_cast<A*>(pb)->SetPublicString() prints In A->Public A

** Don't call virtual functions during construction or destruction, because such calls
will never go to a more derived class than that of the currently executing constructor or
destructor

NULL is just a typedef for 0. Hence fn(int) will be called


** Temp Objects :

Avoid techniqs :
1. //original expression was : string s= s1+s2+s3;
string temp=s1+s2;
temp+=s3;
string s=temp;

2. //original expression was : temp=temp+s3


temp+=s3;

** Copy Ctor
Called in 3 cases,
1. Object being contructed(initialised) by another object.
Normally or ctor init list (incase of composition) both
2. Pass object by value
3. Return object by value

** CCtor and overloaded Ass operator :


For deep copy,but for objects not object-handles

class Base {

public:
int i;
int* ip;
Base(){}

Base(const Base& rhs) // const is not mandatory


{
i = rhs.i;

ip = new int;
*ip = *(rhs.ip);
}

Base& operator =(const Base& rhs) // const is not mandatory


{
if(*this = rhs) return *this; // handle self assgnmnt

i = rhs.i;

delete ip; // delete previous memory to avoid any leaks


ip = new int; // assign fresh memory block
*ip = *(rhs.ip); // fill the value in that block
return *this;
}
};
** placement new

1. Building a custom-made memory pool or a garbage collector.


2. For critical applications since it’s a fail-safe way of allocation since memory is
pre-allocated.
3. When we want allocation on a specific hardware address.
4. Faster since it takes lesser time in creating object on pre-allocated buffer
char *pbuff = new char[1024]; // heap allocation using plain new
Person *p = new (pbuff) Person; // placement new uses a pre-allocated
Buffer

P -> ~Person(); // Later and explicitly, to free memory

char pbuff [1024]; //bad idea: stack allocation instead of heap


Person *p = new ( pbuff ) Person; //undefined behavior

caveats :
1. We must ensure that the memory is big enough/valid
2. We must ensure that the memory is aligned
3. We must ensure that the memory is deallocated (call dtor explicitly . Even placement
delete is useless here since it’s for different purpose)

** placement delete :
Placement delete is not supposed to be called directly by the user .Rather, it merely
serves as the deacllocation function that the implementation must invoke when an
exception occurs during the construction of an object by placement new. To generalize,
for every version of operator new, C++ provides a matching operator delete. Since there
are six versions of operator new (ordinary new, placement new, nothrow new, as well as
their array counterparts), there are also six corresponding versions of delete.

As opposed to a common belief, placement delete does not invoke the destructor of its
argument; in fact, it has no effect. To ensure that an object constructed by placement new
is destructed, you need to call its destructor explicitly.

** Ctor Init List:

1. excp hdling is possible


class Foo
{
Foo() try : _str( "text of string" )
{
}
catch ( ... )
{
std::cerr << "Couldn't create _str";
// now, the exception is rethrown as if we'd written
// "throw;" here
}
};

2. It is important to remember that irrespective of the order of the initialization in the


initialization list, the class members are initialized in the order in which they appear in
the class, after the base class members are initialized.
3. Passing params to base classes
4. Init const/ref/const static members
5. in-situ initlzn of member data too.Optimez than in {} assignment
approach,for objects not built-in.

6. Cant access this ptr.


7. No error checking possible

** Prefer Prefix operator since in the postfix case, the value is increased, but the old
value is returned. So one extra temp is created.
Prefer += instead of + <same reason,temp avoid>

7) What is your opinion on the code below?

class B
{
public:
B(int x = 0) : mx(x){cout << "1";}
B(const B& rhs){cout << "2"; mx = rhs.mx;}
B& operator=(const B& rhs)
{cout << "3"; if(this == &rhs) return *this; mx = rhs.mx; return *this;}
operator int(){cout << "4"; return B::mx ;}
~B(){cout << "5";}
private:
int mx;
};

int main()
{
B b;
b = 2 + b;
return 0;
}

c)This code will print 14135


Sorry, the correct answer is c. "This code will compile properly. What happens is that
object b is converted to an int using B::operator int(). This operator returns the value
b::mx which is 0 in our case. Now the addition is performed (2+0) which yields 2. Now
the value of 2 has to converted back to a B object. Hence a temporary B object is created
using the constructor B(int x = 0).
Subsequently b is assigned to this temporary object using B::operator= "

**When a class overloads operator new and delete, these have to be static. This is
because these operators are called before the object is create and after it has been
destroyed respectively.
Alternatively it makes sense to think that these operators know how to new and delete
objects of a given class rather than on a "per object" basis. "

** "It is important to remember that if the base class overloads operator new and operator
delete, the same will be called when creating objects of derived class also. In this case the
sizeof(B) is 8. Hence A::operator new is called with size 8 while allocating p. When p is
destroyed, A::operator delete is called with the size parameter as 4. This is because A has
not defined it's destructor as virtual. Modify this code and make the destructor as virtual
and check the program output. Calls to operator new and operator delete for p1 are as
expected."
---------
What is your opinion on the code below?

class A
{
public:
virtual A& print(){cout << "1"; return *this;}
void fn(){cout << "2";}
};

class B : public A
{
public:
A& print(){cout << "3"; return static_cast<A&>(*this);}
void fn(){cout << "4";}
};

class C: public A
{
B& print(){static B b; cout << "5"; return b;}
};

int main()
{

C c;
A &aref = c;
aref.print();
B b;
A &aref2 = b;
aref2.print();
aref2.fn();
return 0;
}

a)This code will give compilation error as the overloaded functions differ only in their return type
b)This code will print 532
c)This code will print 332
d)This code will print 534

Sorry, the correct answer is b. Remember that the only exceptional case in which
virtual functions signature can be different in the derived class is if the return type
of the base class (virtual) function is a public base class of the return type of the
derived class function. In this case, even if the function signatures are different, they are
still treated as polymorphic. That is the reason aref.print call is routed to C::print rather
than A::print. Also note that C::print is private. However since the call to C::print is
routed via the VTABLE the access restriction does not hold good in this case, and the
private function is called.

**Virtual inheritance:
Mechanism whereby a class specifies that it is willing to share the state of its virtual base
class. Under virtual inheritance, only one, shared base-class subobject is inherited for a
given virtual base regardless of how many times the class occurs as a virtual base within
the derivation hierarchy. The shared base-class subobject is called a virtual base class.

Ordinarily each class initializes only its own direct base class(es). This initialization
strategy fails when applied to a virtual base class. If the normal rules were used, then the
virtual base might be initialized multiple times. The class would be initialized along each
inheritance path that contains the virtual base. To solve this duplicate-initialization
problem, classes that inherit from a class that has a virtual base have special
handling for initialization. In a virtual derivation, the virtual base is initialized by
the most derived constructor.
Although the virtual base is initialized by the most derived class, any classes that inherit
immediately or indirectly from the virtual base usually also have to provide their own
initializers for that base. As long as we can create independent objects of a type derived
from a virtual base, that class must initialize its virtual base. These initializers are used
only when we create objects of the intermediate type.
Another important thing to remember is that virtual base classes are always
constructed prior to non-virtual base classes regardless of where they appear in the
inheritance hierarchy.
Examples of virtual inheritance are plenty in the standard library. istream and
ostream virtually inherit from ios. iostream multiply inherits from istream and
ostream.

class Base {
public:
int i;

Base(int a) {cout<<"1"<<endl;
i = a;}

};

class Der1 : public virtual Base {


public:

Der1(int a):Base(a){cout<<"2"<<endl;}
};

class Der2 : public virtual Base {


public:

Der2(int a):Base(a){cout<<"3"<<endl;}
};

class Der : public Der1,public Der2{


public:

Der(int a,int b,int c): Der1(a),Der2(b),Base(c){cout<<"4"<<endl;}

};

class D : public Der{


public:

D(int a,int b,int c,int d): Der(a,b,c),Base(d) {cout<<"5"<<endl;}

};

int main()
{

Der1 d(11);
cout<<d.i<<endl;  Base::i = 11

D d(1,2,3,99);
cout<<d.i<<endl;  Base::i = 99

cin.get();
return 0;
}
** Templates: On demand, New code for every new type (but just one for one type)

From the point of view of the compiler, templates are not normal functions or classes.
They are compiled on demand, meaning that the code of a template function is not
compiled until an instantiation with specific template arguments is required. At that
moment, when an instantiation is required, the compiler generates a function specifically
for those arguments (argument types to be precise) from the template.
Thus might lead to code bloat if used indiscriminately.
Better than macros,for sure.

Same behaviour for all/diff data types : prefer Templates ex : multiply() for diff types
Diff behaviour and/or diff data types : prefer Inheritance ex: Shape hierarchy draw()

Template class can have both templatized and non-templatized member functions.

Non-type template parameters must be of integral, enumeration, pointer, reference, or


pointer to member type, and must be constant at compile time.
They can be qualified as const or volatile types.
Floating point values are not allowed as non-typed template parameters.
String literals are not allowed….
Objects of class, struct or union type are not allowed as non-type template parameters,
although pointers to such objects are allowed.
Arrays passed as non-type template parameters are converted into pointers.
Functions passed as non-type parameters are treated as function pointers.

Remember that non type parameters of a template are not L-values. They can not
be modified inside the class. “Const”

Note that when friendship is granted to a function template or a class template, it


becomes the case of unlimited friendship. Friendship is granted to all instantiations of the
function or class template.

Static member variables of a class template are shared across all instantiations of a
given type. This means that objects of type Foo<int> will all share the same static
member, whereas objects of type Foo<double> will all share the same static
member, which is different from the static member of type Foo<int>

---
A dependent name is a name that depends on the type or the value of a template
parameter.
Dependent names technically exist outside of template definitions as well, but most
discussion of dependent names involves the need for disambiguation of dependent names
within template definitions.
The compiler binds dependent names when a template is instantiated. The compiler binds
non-dependent names when a template is defined

Ex:
template<class T> class U : A<T>
{
typename T::B x;
void f(A<T>& y)
{
*y++;
}
}; // The dependent names in this example are the base class A<T>,
// the type name T::B, and the variable y.
--------
typename keyword:

rules :

• It appears inside a template


• It’s qualified
• It isn’t used in a base class specification or a member initialization list.

2 uses:
1. Interchangeable with class keyword in template declaration
2. To inform compiler that the name used in template declaration is a type name and not
an object name. ex: typename X::Name someObject;
i.e, typename T::iterator * iter;

since, otherwise this might get parsed as object name and not type name (if there is such a
object in scope) and lead to compile error,

class ContainsAType {
class iterator { ... }:
...
};

template <class T>


void foo() {
T::iterator * iter;
...
}

foo<ContainsAType>();  OK , but what if following class is present

class ContainsAValue {
static int iterator;
};

Then, this might be treated as multiplication between static variable


iterator and some variable iter !! A compile error ensues.
So, better use typename qualifier always.
export keyword:
Can precede a in template declaration. It allows other files to use a template declared in a
different file by specifying only its declaration rather than duplicating its entire
definition.
---------

Class template specialization :


The idea of C++ class template specialization is similar to function template
overloading. This can make the template code for certain data types to be fixed.
Done if deemed that specific data type class must be written due to,
1. Data type specific (additional) functionality needs to be given
2. Specific data type instantiation (based on the generic template definition) might not
compile
3. Partial : you write a template that specializes on one feature but still lets the class user
choose other features as part of the template.

1. Full/Explicit template specialization:


template <>
class mycontainer <char> {… , can define additional specific funcs
here};

template<>
void sort<char*>(Array<char*>&);  Function Template

2. Partial class template specialization:


template <typename T>
class mycontainer <T,double>{};
ex:
// template specialization
#include <iostream>
using namespace std;

// class template:
template <class T>
class mycontainer {
T element;
public:
mycontainer (T arg) {element=arg;}
T increase () {return ++element;}
};

// class template specialization:


template <>
class mycontainer <char> {
char element;
public:

mycontainer (char arg) {element=arg;}


char uppercase ()
{
if ((element>='a')&&(element<='z'))
element+='A'-'a';
return element;
}
};

int main () {
mycontainer<int> myint (7);
mycontainer<char> mychar ('j');
cout << myint.increase() << endl;
cout << mychar.uppercase() << endl; return 0;}

// Non-template class can have a templatized function


class mypair {
int i;
public:
mypair (int first)
{
i = first;
}
template <class T>
void show(T ex){cout<<ex<<endl;}
};

Disadv:
1. Since the compiler generates additional code for each template type (or, class for
a type), indiscriminate use of templates can lead to code bloat, resulting in larger
executables.
2. Still some compilers don’t have standard support for templates.
3. Can’t have them distributed as libraries.All the impl code mite desirably be in
headers since they are not concrete functions/classes but generic ones/patterns.
(In order for the compiler to generate the code, it must see both the template
definition (not just declaration) and the specific types/whatever used to "fill in"
the template. For example, if you're trying to use a Foo<int>, the compiler must
see both the Foo template and the fact that you're trying to make a specific
Foo<int>)

2 aproaches :
1. Inclusion Model : put def in header itself, code bloat but cleaner

2. Explicit Instantiation : Put directive in .cpp file. This avoids code bloat but
it’s a manual approach
#include "myfirst.cpp"
// explicitly instantiate print_typeof() for type double
template void print_typeof<double>(double const&);

// explicitly instantiate class Stack<> for int:


template class Stack<int>;

Alternatively, can have typedef in headers also.Or, export


keyword.

-----------------------------------------------------------------------------------
** Returning ptr/ reference from a func, problems :
1. If you have local data in a function, you really ought not return a reference to it at all
(unless it is static) since it will be be a reference to memory that is no longer valid.
2. You are returning a reference to member data of an object. Although returning a const
reference prevents anyone from changing the data by using it, it means that you have to
have persistent data to back the reference--it has to actually be a field of the object and
not temporary data created in the function
3. if its ref/ptr to const, then it can be assigned to anything non-const so this wont work :

(w1 = w2) = w3; // objects


(w1 = w2) = w3; // ints, since it works so lets not return handle-to-const to keep in
// synch with this

** What is the output of this program?


class MyClass
{
int m_nVal;

public:
MyClass(int nVal = 0) : m_nVal(nVal){
}

MyClass& operator++(){
cout << "In operator++()" << endl;
m_nVal++;
return *this;
}

MyClass(const MyClass& rhs)


{
cout << "In Copy Constructor" << endl;
m_nVal = rhs.m_nVal;
}

MyClass operator++(int){
cout << "In operator++(int)" << endl;
MyClass ret(*this);
++*this;
return ret;
}
};

int main(){

MyClass cls(0);
cls++;
++cls;

return 0;
}

c) In operator++(int)
In Copy Constructor
In operator++()
In operator++()

* EBO (Empty Base Class Optimizn)

1. composition:

class Empty {};

class HoldsAnInt {
private:

int x;
Empty e; // should require no memory
};

But : sizeof(HoldsAnInt) > sizeof(int) !!

2. Private inheritance

class HoldsAnInt: private Empty {


private:

int x;

};

 sizeof(HoldsAnInt) = sizeof(int)

* Use of Private Inheritance:


1. Impl inheritance
2. EBO
3. Need to override some base funcnlty

* Exceptions/ Special Features in C++:

1. Privates of base accessible to derived via vtable mechanism.


2. Co-variant return types
3. Virtual Inheritance and its init process
4. Plain/Static func: class scope, can be called using unassigned class pointer
Virtual func: object scope,gives run time exception
5. virtual dTor : otherwise call using base ptr will call base version only,derived version
won’t be called thus derived object portion still unallocated thus Memory
Leak.
6. Dtor in private : Still objects can be made on heap (not on stack ofcourse),have to
provide a custom dtor func then, technique used to create heap only
class.

7. CCtor in derived class to handle base class types copy -> POSSIBLE !!

Ex:
class A
{
public:
A(int n = 0) : mnA(n){}
A(const A& rhs){mnA = rhs.mnA;}
A& operator=(const A& rhs)
{if(&rhs == this) return *this; mnA = rhs.mnA; return *this; }
virtual ~A(){}
protected:
int mnA;
};

class C: public A
{
public:
C(int n = 0) : mnC(n), A(n){}
C(const C& rhs){mnC = rhs.mnC;}
C& operator=(const C& rhs)
{if(&rhs == this) return *this; mnC = rhs.mnC; return *this;}
C& operator=(const A& rhs)
{if(&rhs == this) return *this; (A&)(*this) = rhs; return *this;}
void print()
{cout << A::mnA << mnC;}
virtual ~C(){}
private:
int mnC; };

int main()
{
C c(2);
C c2(c);
C c3;
C c4;
A a(2);
c3 = c;
c4 = a;
c.print();
c2.print();
c3.print();
c4.print();
return 0; }  o/p : 22 02 02 20 if we had not given CCtor/Ass Op then
compiler would have done bit wise copy and
o/p : 22 22 22 2

Sorry, the correct answer is b. "It is important to note that the derived class assignment
operator and the copy constructor should take care of base class members also. That does
not happen automatically. In this case there are two assignment operators in class C. The
assignment operator which takes const C& does not take care of base class assignment.

c.print() prints 22.

c2 is copy initialized from c through the copy constructor of C which does not take care
of base class member. Hence c2.print() prints 02.
Note that had we not declared our own copy constructor the compiler synthesized copy
constructor would have initialised the base class members also (bit wise copy).

c3 is assigned to c and this involves the operator=(const C&). This also does not take care
of the base class members. Hence this also prints 02.

Finally c4.print() prints 20. This is because this assignment invokes operator=(const A&)
and this takes care only of the base class member.

8. Typecast Lvalue , ex : (A&)(*this) = rhs;


9. Virtual mechanism can be used with pointers and references.
10. Implicit Ctor call (use explicit to avoid this)
Ex :
Class B {
B(int x) {cout << "cc";mx = x; } // must have this parameterized Ctor one
// unavoidable use
};

Main(){
B b = 1; // Else these 2 lines won’t compile at all
Or B b; b = 2;
}

11. temp t; temp y = t; -> calls CCtor not Ass oprtr (obviously)
12. in Cctor and assnmnt op : not necc to have params as const&
13. We must return *this in overloaded ass op otherwise normal assigmnt wud work but
chain of assignment will/might fail espclly if we return void.

14. If you do not specify your own set_new_handler() function, new throws an
exception of type std::bad_alloc.
15. Avoid overloading on a pointer and a numerical type.
16. Avoid data members in the public interface.Since functions are a better/flexible
abstraction.

** Absurdities in C++:

1. Virtual cTor : 1. Trying to call derived version of ctor using existing base ptr to
derived object. Non sense !! still wanna do -> factory method pattern.
2. virtual Static func : since static is class scope and mutually exclusive from virtual func
concept.
3. virtual friend func : But approx would be like :

An example of where the equivalent of a virtual friend would be useful is if serializing an


entire class hierarchy. The trick in doing this is to turn the friend function into a
lightweight inline proxy function, which forwards the request back to the class. For
example, consider the simple base class:

class CPerson
{
friend ostream &operator<< (ostream &out, CPerson &person);

// ...

protected:
virtual ostream &display(ostream &out);
// ...

};

The friend function forwards the display request back to the CPerson class. Note that we
pass an instance of CPerson by reference:

inline ostream &operator<< (ostream &out, CPerson &person)


{
return person.display(out);
}

In a class hierarchy using the base CPerson class, each derived class has its own
implementation of the display function:

class CBob : public CPerson


{
protected:
virtual ostream &display(ostream &out)
{
out << "Bob";
return out;
}
};

**
class Shape {
public:

virtual void create() {cout<<"s"<<endl;} // Uses the default constructor


};

int main()
{
Shape* cp;
cp -> create();  run time error !! since virtual funcns are object associated unlike
plain/static funcns

}
------
Overload iostream ops :

ostream& operator << (ostream& ot, const char* chr)


{
using std::operator<<;
ot << "Hai Arvind " << chr << " 99999999999";
return ot; }

1.Prevent instantiation : give a PVF

2. Stack Only :

class autoOnly
{
public:
autoOnly()
{
;
}
~autoOnly()
{
;
}
private:
void* operator new(size_t)
{
// Dummy Implementation
}

void* operator new[](size_t)


{
// Dummy Implementation
}
void operator delete(void*)
{
// Dummy Implementation
}
void operator delete[](void*)
{
//Dummy Implementation
}
};

3. Heap Only : (caveat : give func for delete, its lets on heap !!!)

class HeapOnly
{
public:
void DestroyMe() //This function will call destructor
{
delete this; // Current object will be deleted means destructor
}
private:
~HeapOnly(); // destructor only can be call by member function
};

main()
{
HeapOnly* h = new HeapOnly(); // fine
HeapOnly h; // error !!
}

4. Prevent Derivation : 2 approaches


4.1 make dtor private and give default ctor in derived, but it becomes heapOnly class

4.2 exploit mix of V-inheritance and 4.1 technique

class temp
{
private:
~temp() {; }
friend class Final; // Due to friend , Final class can use private
member functions of temp class
};
class Final: virtual public temp // if not virtual inheritance then
// still Child can be derived from
// Final as a heap only class
{
// Define all data members and functions as you want
public:

Final()
{
;
}
~Final();
};

class Child : public Final


{
public:
Child(){ }
~Child() { }
};
** Virtual ctor: (Factory method pattern)

class Shape {
public:
virtual ~Shape() { } // A virtual destructor
virtual void draw() = 0; // A pure virtual function
virtual void move() = 0;
.. .
virtual Shape* clone() const = 0; // Uses the copy constructor
virtual Shape* create() const = 0; // Uses the default constructor
};

class Circle : public Shape {


public:
Circle* clone() const; // Covariant Return Types; see below
Circle* create() const; // Covariant Return Types; see below
...
};

Circle* Circle::clone() const { return new Circle(*this); }


Circle* Circle::create() const { return new Circle(); }

void userCode(Shape& s)
{
Shape* s2 = s.clone();
Shape* s3 = s.create();

//and for Circle* cp = s.clone();  gives error, need to downcast as //


(Circle*) as it still treats return type as Shape* ONLY
...
delete s2; // You need a virtual destructor here
delete s3;
}

** NCI (Named Ctor Idiom)

#include <cmath> // To get sin() and cos()

class Point {
public:
static Point rectangular(float x, float y); // Rectangular coord's
static Point polar(float radius, float angle); // Polar coordinates
// These static methods are the so-called "named constructors"

private:
Point(float x, float y); // Rectangular coordinates
float x_, y_;
};

inline Point::Point(float x, float y)


: x_(x), y_(y) { }

inline Point Point::rectangular(float x, float y)


{ return Point(x, y); }

inline Point Point::polar(float radius, float angle)


{ return Point(radius*cos(angle), radius*sin(angle)); }

int main()
{
Point p1 = Point::rectangular(5.7, 1.2); // Obviously rectangular
Point p2 = Point::polar(5.7, 1.2); // Obviously polar
...
}

** AutoPtr Class

template<class T>
class auto_ptr {
private:
T* ap; // refers to the actual owned object (if any)
public:
typedef T element_type;

// constructor
explicit auto_ptr (T* ptr = 0) throw()
: ap(ptr) {
}

// copy constructors (with implicit conversion)


// - note: nonconstant parameter
auto_ptr (auto_ptr& rhs) throw()
: ap(rhs.release()) {
}
template<class Y>
auto_ptr (auto_ptr<Y>& rhs) throw()
: ap(rhs.release()) {
}

// assignments (with implicit conversion)


// - note: nonconstant parameter
auto_ptr& operator= (auto_ptr& rhs) throw() {
reset(rhs.release());
return *this;
}

template<class Y>
auto_ptr& operator= (auto_ptr<Y>& rhs) throw() {
reset(rhs.release());
return *this;
}

// destructor
~auto_ptr() throw() {
delete ap;
}

// value access
T* get() const throw() {
return ap;
}
T& operator*() const throw() {
return *ap;
}
T* operator->() const throw() {
return ap;
}

// release ownership
T* release() throw() {
T* tmp(ap);
ap = 0;
return tmp;
}

// reset value
void reset (T* ptr=0) throw() {
if (ap != ptr) {
delete ap;
ap = ptr;
}
}

/* special conversions with auxiliary type to enable copies and assignments


*/
auto_ptr(auto_ptr_ref<T> rhs) throw()
: ap(rhs.yp) {
}
auto_ptr& operator= (auto_ptr_ref<T> rhs) throw() { // new
reset(rhs.yp);
return *this;
}

template<class Y>
operator auto_ptr_ref<Y>() throw() {
return auto_ptr_ref<Y>(release());
}
template<class Y>
operator auto_ptr<Y>() throw() {
return auto_ptr<Y>(release());
}
};
}

-------------------------------------------------------------------------------

** SharedPtr Class (RCSP)

class Pointer
{
public:

//standard construction and destruction:


Pointer () {
cout<<"Pointer::default constructor = \n";
}

Pointer (const Pointer& rhs) {


cout<<"Pointer::copy constructor = \n";
myVal_ = rhs.myVal_;
}

Pointer& operator = (const Pointer& rhs) {


cout<<"Pointer::operator = \n";
if(this != &rhs)
{
myVal_ = rhs.myVal_;
return (*this);
}
}

~Pointer (void){
cout<<"Pointer::Destructor\n";
}

//reference counting related methods


void acquire(void){
++refCount_;
} // increase refCount

unsigned int release(void){


return(--refCount_);
} //decrease refCount

//accessor
unsigned int count(void){
cout<<"Reference count value is ="<<refCount_<<"\n";
return (refCount_); //return current count
}

private:

//data
int myVal_;
unsigned int refCount_; // reference counting related data

}; // Pointer

// Here is our own SmartPointer class for Pointer class

class SmartPointer {
public:

// standard construction and destruction:


SmartPointer(Pointer *ptr):ptr_(ptr){
cout<<"SmartPointer::default constructor = \n";
ptr_->acquire();
}

SmartPointer(const SmartPointer &rhs):ptr_(rhs.ptr_){


cout<<"SmartPointer::copy constructor = \n";
ptr_->acquire();
}

SmartPointer& operator = (const SmartPointer &rhs){


cout<<"SmartPointer::operator = \n";
if (this != &rhs)
{
ptr_->release();
ptr_ = rhs.ptr_;

ptr_->acquire();
return(*this);
}
}

~SmartPointer (void){
cout<<"SmartPointer::Destructor\n";
if (NULL != ptr_)
{ if (0 == ptr_->release())
{
ptr_->count();

delete(ptr_);
ptr_ = NULL;
}
}
}

Pointer* operator -> (void) {


return (ptr_);
}

Pointer& operator * () const {


return(*ptr_);
}

private:
Pointer * ptr_;
}; // SmartPointer

// client uses SmartPointer as a Stack Variable here


int main()
{
{
//sp1 acquires refCount now. so refCount = 1
SmartPointer sp1 = SmartPointer( new Pointer());
sp1->count();
{
//sp2 refers sp1 and acquires refCount now. so refCount = 2
SmartPointer sp2 = sp1;
sp1->count();
} // at this point sp2 relases refCount. so refCount = 1
sp1->count();
} // at this point sp1 relases refCount. so refCount = 0 and so here the ptr_ gets deleted

return(0);
}

Potrebbero piacerti anche