C++ Medium Interview Questions – part2


This post is a part of an ongoing series of C++ Interview Questions. This is Second part of the two parts of C++ interview questions with medium difficulty.

These questions were handpicked by the author, who happens to be an active member in one of the largest C++ communities in Orkut which boasts to have over 146,000 members.

6. What are pure virtual functions? Give a real-world example for abstract class and pure virtual function?

A pure virtual function is a virtual function that doesn’t require a definition in the base class. A pure virtual function is defined by writing =0 after its signature. The definition can be provided if some default behavior or error-handling is required.

In some cases it’s not logical to instantiate an object of a class. In such cases, the concept of abstract classes can be made use of. To make a class abstract, the class needs to include at least one pure virtual function.

For Example:

Consider a class Employee that defines the generic behavior of different types of Employees that work in a company.

#include<iostream>
#include<string>

using std::string;

class Employee
{
      string name;
      string Emp_num;

protected:
      int Basic;
      float allowance;

public:
      Employee(string _name, string _num, int _base, float allow): name(_name), Emp_num(_num), Basic(_base), allowance(allow){}

      virtual ~Employee(){}

      virtual float salary()=0;
      int getBasic(){ return Basic; }
      std::string getName(){ return name; }
};

class Manager: public Employee
{

public:
      Manager(string name, string Emp_num, int Base): Employee(name, Emp_num, Base, 3.2){}

      Manager(string name, string Emp_num, int Base, float allow): Employee(name, Emp_num, Base, allow){}

      float salary()
      {
            float sal;

            /*Calculations for Manager's salary*/
            sal = Basic + (allowance/100)*Basic;
            return sal;
      }
};

class Executive: public Employee
{

public:
      Executive(string name, string Emp_num, int Base): Employee(name, Emp_num, Base, 1.2){}

      Executive(string name, string Emp_num, int Base, float allow): Employee(name, Emp_num, Base,allow){}

      float salary()
      {
            float sal;

            /*Calculations for Executive's salary*/
            sal = Basic + (allowance/100)*Basic;
            return sal;
      }
};

int main()
{
      Employee *Mgr= new Manager("Saurabh","M001", 50000);
      Employee *Exec = new Executive("Ajay","E002", 20000);

      std::cout<<"Manager's salaryt: "<<Mgr->salary();
      std::cout<<"nExecutive's salaryt: "<<Exec->salary();
}

Here, the Employee has been made an abstract class because every Employee needs to be one of Manager, Executive, Trainee, etc.. The existence of an Employee in itself doesn’t tell you where the Employee stands in the hierarchy.

7. What is a template?

Templates in C++, are a way to achieve generic nature in programming. By using templates, we can define functions and classes that show the same( or similar) behavior on different types.

For Example:

#include<iostream>

template<typename T>
void swap(T&amp;amp; obj1, T&amp;amp; obj2)
{
      T temp = obj1;
      obj1 = obj2;
      obj2 = temp;
}

int main()
{
      int val1=20, val2=50;
      std::cout<<"Before Swap:t";
      std::cout<<"val1 = "<<val1<<" val2 = "<<val2<<std::endl;
      swap(val1, val2);
      std::cout<<"After Swap:t";
      std::cout<<"val1 = "<<val1<<" val2 = "<<val2<<std::endl;
}

Note: Don’t use this swap(T&,T&) to swap container objects, because copying and assignment may cause heavy overheads. Instead, use the swap() function provided by the container itself.

For Example:

template<typename T>
class List
{
      struct Link{ T data; Link* next;};
      Link head;
public:
      void insert(T data) { /*Add new node*/ }
      void remove()       { /*Remove a node*/ }
      T fetch()             { /*Return first node data*/ }
};

Now, one can have a List of ints, List of chars, List of strings, etc..

8. What is the conversion operator?

The conversion operator is used to convert a user-defined type to some other type.

Consider a class smallInt that is used to store integers in range [0,5000]. Objects of such type would be constantly exposed to arithmetic operations.

To access the integer value of the object, we can

  1. use accessor methods
  2. use conversion operator

Using an accessor method again and again in an expression would invoke unnecessary invitations to explicit function calls and the code would become procedure-oriented rather than data-oriented.

Instead we can define a conversion operator that will fetch the integer value wrapped in the smallInt object whenever required.

#include<iostream>
#include<stdexcept>

class smallInt
{
      int value;
public:
      smallInt(): value(0){}

      smallInt(int x): value(x)
      {
            if(x>5000 || x<0)
                  throw std::out_of_range("smallInt Range: 0-5000");
      }

      operator int(){ return value;}
};

int main()
{
    try{
        smallInt obj500(500), obj33(33);
        std::cout<<obj500*obj33;
        smallInt object = obj500*obj33;        //throws exception
    }catch(std::out_of_range&amp;amp; e){std::cout<<"n"<<e.what();}
}

PS: out_of_range can be accessed using the header stdexcept

9. What are the different casts supported by C++?

C++ supports a variety of casts. They can be listed as :

  1. C-style cast
    float myFloat=24.829;
    int myInt = (int)myFloat;
  2. Function-style cast
    int myInt = int(myFloat);

    The above two casts are not reliable and should be used to cast between built-in types only.

  3. static_cast < T > (f)
    These are less dangerous than the C-style casts. Should be used when an implicit conversion from T to F or vice-versa exists. This can be used to cast numeric types or for upcasting class objects.
    Also, these can be used to convert a base-class pointer to a derived-class pointer, but as it is done during compile-time, the dynamic type is not known. Hence this cast can invite trouble.

    Example 1:

    class base
    {
          /*Members*/
    };
    
    class derived: public base
    {
          /*Members*/
    };
    
    base* ptr_bb = new base;
    base* ptr_bd = new derived;
    
    /*Conversion 1*/
    derived* ptr_db = static_cast<derived*>(ptr_bb);
    
    /*Conversion 2*/
    derived* ptr_dd = static_cast<derived*>(ptr_bd);
    

    This would compile fine but during run-time it might cause problems because in Conversion 1, ptr_bb points to an object of its Base-type and not to its derived.
    Conversion 2 is safe because ptr_bd points to a derived object which can be well-handled using ptr_dd which is a pointer to derived type.

    Example 2:

    float myFloat = 24.222;
    int myInt = static_cast(myFloat);

  4. dynamic_cast < T >(f)
    This cast is type-safe in nature. It’s used to downcast pointers and references to their base class safely. This cast is done at run-time and returns the correct sub-object if the cast was possible, else returns 0 and throws a bad_cast exception. It is required that there be at least one virtual function in the base class.

    For Example:

    base* ptr_b1 = new base;
    base* ptr_b2 = new derived;
    
    /* Conversion 1. ptr_d1 will be zero*/
    derived* ptr_d1 = dynamic_cast<derived*> ptr_b1;
    
    /* Conversion 2. ptr_d2 will be valid*/
    derived* ptr_d2 = dynamic_cast<derived*> ptr_b2;
    
  5. reinterpret_cast < T >(f)
    This type of cast is a very rarely used cast, because the after-effect is implementation-defined,i.e., . It treats the pointers and references as incomplete types. The casted value obtained should be used only after casting back to its original type.
  6. const_cast < T >(f)
    This cast is used to cast away the const-ness of objects or pointers. This should be used only when one is sure that the underlying object was not defined to be a const. If the underlying object was a const, it would invoke Undefined Behavior.

10. What is the auto_ptr? Why is it required?

The auto_ptr is a type declared in the <memory> header. It is used to get the basic Resource Acquisition is Initialization(RAII) features.

The RAII features are required to write an exception-free code. This is because if a function throws an exception, then stack unwinding takes place and it destroys the objects on the stack, but not those that are allocated dynamically.

Hence, we can create a type that will wrap the pointer and use accessor functions to access the memory. As the object would be on the stack, its destructor would be called during the stack unwinding and hence, the dynamically allocated memory is freed.

For Example:

#include<iostream>
#include<memory>

int main()
{
    std::auto_ptr<int> x(new int);
    std::cin>>*x;
    std::cout<<*x;
}

About the Author:  Saurabh Manchanda is a whiz C/C++ programmer. He is currently pursuing his Bachelors of Technology degree in Computer Science. His passions are programming, studying algorithms and solving puzzles. He loves reading books, technical textbooks and fiction novels. When not programming, he could be seen playing pool or cricket or watching movies, listening to songs or staring at his pet fishes! You can connect him at Orkut, Facebook, follow him on Twitter or contact him at saurabh29789@gmail.com If you are thankful for his posts, you may want to gift an item from his Amazon Wish List. Read more from this author



All comments of post - "C++ Medium Interview Questions – part2":

:Haha! I'am the first! Yeh~

Thank you!

Add a Comment / Trackback url

Comment begin from here or jump up!