简体   繁体   中英

Not Inherit from Base Class C++

Let me get this straight. This is an interview question which I am not able to solve till now.Consider two classes

class A
{
     public : virtual int f() { };
              int a;
 }

class B : public A
{

     public : virtual int g() { };
              int b;
}

When asked the size of A and BI said correctly 8 and 12. The next question was how to define the class B so that it neglects the first 8 bytes derived from A. He said this is possible. I still can't understand how it is possible. Can anybody explain how this can be done?

Edit : The real question is not finding the size of classes but the followup.

I'm not sure what answer the questioner was expecting, but here's some possible solutions:

Make 'A' a pointer:

//Only takes 4 or 8 bytes (32 bit vs 64 bit code) for 'A', regardless of 'A's actual size, but must point to an 'A' located elsewhere in memory with the full size.
class B
{
   A *a; //Only 4 bytes.
   int b;
};

Make 'A' static:

//Doesn't assume any of 'A's size, but all instances of 'B' shares a single instance of 'A'.
class B
{
    static A a;

    int b;
};

Pass 'A' into the functions of 'B':

//Pass in the reference to 'a' when needed, so multiple 'B's can share the same 'a' explicitly when desired.
class B
{
    void q(A &a) { this->b + a.a; return a.g(); }
};

Make 'A' and 'B' not have virtual tables (probably the interviewer's point)

//By not having virtual functions, you save the (really small) cost of 4 bytes (8 bytes on 64 bit machines)
class A
{
public:
     int f() { }; //Not virtual
     int a;
}

class B : public A
{
public:
     int g() { }; //Not virtual
     int b;
}

It still costs you the size of A::a, and, unless you re-use 'a' in B instead of having B::b, you can't avoid those 4 bytes. And re-using one variable to mean something else entirely is a possible sign of really bad programming habits.

Unionize A'a and B's variables and put the functions in a single class

class AB //Only 4 bytes total
{
   public:
   union
   {
       int a;
       int b;
   };

   void f();
   void g();
};

The bad idea about this is, you'll have to keep track of whether you should access 'a' or 'b', because both of them occupy the same 4 bytes of memory, and they can't both be using it at the same time.

Another bad thing about this is that it is a sign that the class has too much responsibility. Is it an A or a B? If it's both, the all important question should be, "Why is it both?". It should have a single-responsibility , not be a monolith of mixed purposes.

Make 'A' a template, and inherit from A<B> :

template<typename TypeB>
class A
{
    int a;
};

//Saves the cost of the virtual table... not that that really matters.
class B : public A<B>
{
    int b;
};

This last one is called the ' curiously recurring template pattern ' (CRTP) The idea is that the inherited ' A<B> ' can call access variables and functions from 'B' (if you pass B's 'this' pointer into A's constructor), and 'B' can access variables and functions directly from ' A<B> '.

You're inheriting from a compile-time generated version of the template 'A', that is generated for 'B'.

This doesn't directly answer the interviewer's question, but another possible way manipulating the inheritance of A and B is to do something like this:

#include <iostream>
#include <memory>
#include <vector>
using namespace std;

//This concept was taken from a Going Native 2013 talk called "C++ Seasoning" given by Sean Parent
//
//Located here: (about 101 minutes into it)
//http://channel9.msdn.com/Events/GoingNative/2013/Cpp-Seasoning

//Polymorphism without inheritance.
//Permits polymorphism without using pointers or references,
//and allows them to be copied around easier (each instance is actually its own object) rather
//than accidentally shallow-copying when you wanted deep-copies.
//
//Every time Object::Print() is called, it calls
// Object::PrintableConcept::Print(), which virtually calls
// Object::PrintableModel<TYPE>::Print(), which calls your
// "derived" class that implements the Print() function, regardless
// of what that class inherits (if anything).
class Object //Class without inheritance or virtual.
{
    public:
    template<typename Type>
    Object(Type instance) : self(std::make_shared<PrintableModel<Type>>(std::move(instance)))
    { }

    //Calls the "inherited" function.
    void Print() const
    {
        self->Print();
    }

    private:
    struct PrintableConcept //The concept we want to polymorphably access.
    {
        virtual ~PrintableConcept() = default;
        virtual void Print() const = 0;
    };

    //The class that concretely models the concept,
    //and does the actual inheritting.
    template<typename Type>
    struct PrintableModel : public PrintableConcept
    {
        PrintableModel(Type instance) : data(std::move(instance)) { }

        //Every time 
        void Print() const override
        {
            this->data.Print();
        }

        Type data;
    };

    //This should be a unique_ptr, but you also need to make sure
    //you implement proper copy operators in this class and move support.
    std::shared_ptr<PrintableConcept> self;
};

class Whatever
{
    public:
    void Print() const { std::cout << "Whatever\n" << std::endl; }
};

class SomethingElse
{
    public:
    void Print() const { std::cout << "SomethingElse\n" << std::endl; }
};

class WidgetThing
{
    public:
    void Print() const { std::cout << "WidgetThing\n" << std::endl; }
};

typedef std::vector<Object> Document;

int main()
{
    Document document;
    document.emplace_back(Whatever());
    document.emplace_back(SomethingElse());
    document.emplace_back(WidgetThing());

    for(const auto &object : document)
    {
        object.Print();
    }

    return 0;
}

<<< Run the code >>>

None of the classes actually inherit from 'Object' (or from anything else), but can be used interchangeably with each other in, say, a vector, because they all implement a common interface ( PrintableConcept ) that Object accesses templatedly, but Object itself isn't a template and so doesn't become Object<something> and Object<something-else> which would've been separate types.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM