2

让我说清楚。这是一个我现在无法解决的面试问题。考虑两个类

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

class B : public A
{

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

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

编辑:真正的问题不是找到班级的规模,而是找到后续行动。

4

2 回答 2

4

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

使“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;
};

使'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;
};

将 '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(); }
};

让'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;
}

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

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

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

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

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

另一个不好的地方是,这表明这个类有太多的责任。是A还是B?如果两者兼而有之,那么最重要的问题应该是“为什么两者兼而有之?”。它应该有一个单一的职责,而不是混合用途的单体。

将“A”设为模板,并继承自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;
};

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

您继承自为“B”生成的模板“A”的编译时生成版本。

于 2013-10-02T17:02:14.723 回答
0

这并不能直接回答面试官的问题,但操纵 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;
}

<<<运行代码>>>

这些类实际上都不是从“Object”(或其他任何东西)继承的,但可以在向量中彼此互换使用,因为它们都实现了PrintableConceptObject 以模板方式访问的公共接口(),但 Object 本身不是'不是模板Object<something>,因此不会成为Object<something-else>单独的类型。

于 2013-10-02T17:21:54.833 回答