不从基类C ++继承

时间:2022-09-24 23:12:38

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 B I 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?

当被问及A和B的大小我正确地说8和12.下一个问题是如何定义B类,使它忽略从A派生的前8个字节。他说这是可能的。我仍然无法理解它是如何可能的。谁能解释一下如何做到这一点?

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

编辑:真正的问题不是找到类的大小,而是后续。

2 个解决方案

#1


4  

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

我不确定提问者的期待是什么答案,但这里有一些可能的解决方案:

Make 'A' a pointer:

使'A'成为指针:

//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:

使'A'静态:

//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':

将'A'传递给'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)

让'A'和'B'没有虚拟表(可能是面试官的观点)

//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.

它仍然花费你A :: a的大小,除非你在B中重复使用'a'而不是B :: b,否则你无法避免这4个字节。并且重新使用一个变量来完全表示其他东西可能是一种非常糟糕的编程习惯。

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

结合A'a和B的变量并将函数放在一个类中

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.

关于这一点的坏主意是,你必须跟踪你是否应该访问'a'或'b',因为它们都占用相同的4字节内存,并且它们不能同时使用它同时。

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.

关于这一点的另一个坏处是,这表明班级有太多的责任。是A还是B?如果两者兼而有之,那么重要的问题应该是“为什么两者兼而有之?”。它应该具有单一责任,而不是混合目的的整体。

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

将'A'设为模板,并从A 继承:

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>'.

最后一个被称为'奇怪的重复模板模式'(CRTP)这个想法是继承的'A '可以从'B'调用访问变量和函数(如果你把B的'this'指针传递给A的构造函数) ,'B'可以直接从'A '访问变量和函数。

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

您继承了为'B'生成的模板'A'的编译时生成版本。

#2


0  

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:

这并没有直接回答采访者的问题,但是另一种操纵A和B继承的方法是做这样的事情:

#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.

这些类实际上都没有从'Object'(或其他任何东西)继承,但可以在一个向量中互换使用,因为它们都实现了一个对象临时访问的公共接口(PrintableConcept),但是Object本身不是模板,因此不会成为Object 和Object ,它们将是独立的类型。

#1


4  

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

我不确定提问者的期待是什么答案,但这里有一些可能的解决方案:

Make 'A' a pointer:

使'A'成为指针:

//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:

使'A'静态:

//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':

将'A'传递给'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)

让'A'和'B'没有虚拟表(可能是面试官的观点)

//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.

它仍然花费你A :: a的大小,除非你在B中重复使用'a'而不是B :: b,否则你无法避免这4个字节。并且重新使用一个变量来完全表示其他东西可能是一种非常糟糕的编程习惯。

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

结合A'a和B的变量并将函数放在一个类中

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.

关于这一点的坏主意是,你必须跟踪你是否应该访问'a'或'b',因为它们都占用相同的4字节内存,并且它们不能同时使用它同时。

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.

关于这一点的另一个坏处是,这表明班级有太多的责任。是A还是B?如果两者兼而有之,那么重要的问题应该是“为什么两者兼而有之?”。它应该具有单一责任,而不是混合目的的整体。

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

将'A'设为模板,并从A 继承:

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>'.

最后一个被称为'奇怪的重复模板模式'(CRTP)这个想法是继承的'A '可以从'B'调用访问变量和函数(如果你把B的'this'指针传递给A的构造函数) ,'B'可以直接从'A '访问变量和函数。

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

您继承了为'B'生成的模板'A'的编译时生成版本。

#2


0  

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:

这并没有直接回答采访者的问题,但是另一种操纵A和B继承的方法是做这样的事情:

#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.

这些类实际上都没有从'Object'(或其他任何东西)继承,但可以在一个向量中互换使用,因为它们都实现了一个对象临时访问的公共接口(PrintableConcept),但是Object本身不是模板,因此不会成为Object 和Object ,它们将是独立的类型。