Saturday, January 11, 2014

Chapter:07 Supress/Allow Methods

Fundamental
========

In older C++ standard, if we want to disallow client code to use a particular method of a class, we used to define it as 'private' member. This way we can prevent these functions gets called by external client code, but what about the other class functions. I mean by defining as 'private' we can not restrict any other class functions to use their own private functions.


In addition to that if you have written parametrized constructor, then if user code would try to use the default constructor, there would be compilation error. Many time we want to write parametrized constructors and also want to use the  default constructor and related functions which manages class resources. To achieve this we had to write the default constructor of our own. This may lead to some problem in future if we add the new members in this class we had to modify all constructors. There are chances of mistake if we miss out something.


Also if we can not write better code for a particular thing, we better should rely on the default implementation provided by system. Those would be almost most optimized and safer solution in longer term.


C++11 has introduced the concept to allow to 'delete' that particular function. By this mechanism we can prevent any uses of these functions, whether its other class member functions or any client code. For second problem, C++11 has introduced the concept of 'default'. So if you want to make use of default version of the 6 functions(which manages the class resources), you can just mention 'default' and  you would be able to use these default along with your own parametrized constructors and other related functions. These concept along with the in class initializer we  can write very clean and beautiful code for these 6 resource managing functions of a class. I would cover in-class initializer feature in the later chapter.




Uses
===

The current sample program demonstrates that with the use of 'default/delete'  and 'in-class initializer', we can write very elegant code. Here I have defined a parametrized constructor, and also I have enabled the 'default' version of  constructor and destructor. And using in-class initializer, I have made those two variables to be zero. So if client code creates the object of this class with out providing any argument, the default version of constructor would be called. We all know that if we do not enable default version, then there would be compilation error if we do not write our own version.


These things were even possible prior to C++11 as we could have used the parametrized constructor with default values which would have taken care
this situation. But I think that it looks good and clearly distinguish the user version from the default generated version of these 6 functions. I guess even system can make use of this and our code might provide better performance as system can do all sort of tweak and twist to optimize it. I like the C++11 version solution for these instead of older. Hope it make sense to all of us.



//Code Begins

#include<iostream>
class x {
public:
    x(int a, int b):aa(a),bb(b){}
    ~x()=default;
    x()=default;
    void display_element(void) {
        std::cout<<aa<<"\t"<<bb<<std::endl;
    }
private:
    int aa{0};
    int bb{0};
};

void learn_default_allow(void){
    x o_x;
    o_x.display_element();
}

int main(int argc, const char* argv[]) {
    learn_default_allow();
    return 0;
}
//Code Ends



When I run the above program, I get the following output. This was expected and program looks good and elegent.



//Console Output Begins
$ ./test
0    0
//Console Output Ends





We can 'delete' any function of a class. Unlike 'default' which is only applicable to the 6 functions which gets generated by compiler, 'delete' can be used for any memeber function of a class. It is also possible to delete partially for generic function. Here partial means you can allow the same function to gets called for a particular set of arguments and 'delete' for a the rest of the argument. There is no magic behind  this, as we know that compiler generates a unique functions(as if it was hand written) for a particular type of arguments. So in a way  ,there would be different functions loaded when we call the generic function for a particular type of argument. So this is same as if we have called 'delete' for a particular generic function for specified type of arguments.


The current program contains a polymorphic type of class as it has virtual functions. Consider a  scenario, where we define an abstract class(which has at least one pure virtual function). This abstract class has implemented some functions so in a way its not complete abstract type. Normally  these classes would be inherited  by some new class where we would implement the remaining functions which has not been defined in base abstract class. This way C++ supports the incremental design philosophy.


However there seems to be bit problem in the above approach. What if user want to use and test the abstract class at first place itself. I mean after all, if someone want to use and test the existing implemented functionality of that particular abstract class, it would be great I guess. Again this was possible even in older C++ standard where we would simply write blank body of that class so that user would be able to create the object of that particular class.


Here I present a different approach to do the same thing. We can actually 'delete' those functions which has not been  implemented in that particular class. Once we are done with our local testing we can remove the 'delete' from these functions. By this approach also we can create the object  of such class. Here I do not claim that this is correct approach or this approach has some advantages over conventional one. This is just a different one. I always believe that  its always advantageous to know more than one approach to address the same problem.



//Code Begins

#include<iostream>
class y {
public:
    y(int a, int b):aa(a),bb(b){}
    virtual ~y()=default;
    y()=default;
    int add_number(){
        int tmp{};
        tmp=aa+bb;
        return tmp;
    }
    virtual int multiply_number()= delete;
    /* virtual int multiply_number()= 0; */
private:
    int aa{10};
    int bb{20};
};


void learn_default_allow(void){
    y o_y;
    auto res_add = o_y.add_number();
    /* auto res_mult = o_y.multiply_number(); */
    std::cout<<res_mult<<std::endl;
}

int main(int argc, const char* argv[]) {
    learn_default_allow();
    return 0;
}

//Code Ends



When I run the above program, I get the following output. This was expected as we have learned in above discussion.


//Console Output Begins
$ ./test
30
//Console Output Ends




If we go by conventional approach and  comment the 'delete' function and uncomment the two line which I have commented in the above code. By this way if user would like to use this class, compiler would not  be able to create the object as it has one 'pure' virtual function. Here we can not do any uses/testing for this class.




//Console Output Begins
$ g++ -g -gdwarf-2 -std=c++11 -Wall test_2.cpp -o test_2
test_2.cpp: In function ‘void learn_default_allow()’:
test_2.cpp:20:4: error: cannot declare variable ‘o_y’
to be of abstract type ‘y’
  y o_y;
    ^
test_2.cpp:2:7: note:   because the following virtual functions
are pure within ‘y’:
 class y {
       ^
test_2.cpp:13:16: note:     virtual int y::multiply_number()
    virtual int multiply_number()= 0;
                ^
//Console Output Ends




No comments:

Post a Comment