簡體   English   中英

不是從基類C ++繼承

[英]Not Inherit from Base Class C++

讓我說清楚。 這是迄今為止我無法解決的面試問題。請考慮兩節課

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個字節。他說這是可能的。 我仍然不知道怎么可能。 有人可以解釋如何做到這一點嗎?

編輯:真正的問題不是找到類的大小,而是跟進。

我不確定發問者會期待什么答案,但是這里有一些可能的解決方案:

將“ 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”的編譯時生成版本中繼承。

這並不能直接回答訪問員的問題,但是操縱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”(或其他任何東西)的類,但是可以在例如vector中相互互換使用,因為它們都實現了Object可以模板訪問的公共接口( PrintableConcept ),但是Object本身不是模板,因此也不會成為Object<something>Object<something-else> ,它們本來是分開的類型。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM