2017-01-16 12 views
-3

То, что должно показаться простым, стало для меня настоящим камнем преткновения. У меня есть простое наследование, но я хочу, чтобы убедиться, что по крайней мере один экземпляр базового класса существует до построения любого из его производных типов:Убедитесь, что существует хотя бы один экземпляр базы до создания производного типа.

class Parent { 
private: 
    std::string name_; 

    explicit Parent(const std::string& name); 

protected: 
    // Constructor that Derived Types Will Use 
    Parent(const std::string& parentName, const std::string& childName, bool isChildAParent = false); 

}; 

Child : public Parent { 
public: 
    Child(const std::string& parentName, const std::string& childName, bool isChildAParent = false); 
}; 

// It is here in the 2nd or protected constructor that I'm struggling with. 
Parent::Parent(const std::string& parentName, const std::string& childName, bool isChildAParent) { 

    // How To Go about to check if an instance of Parent already exists that 
    // used its default constructor before using this constructor from a derived type? 

} 

В основной обычаи будет выглядеть так:

int main() { 
    Child child("parent", "child", false); // Invalid since Parent wasn't created first 

    Parent parent("parent"); 
    Child child("parent", "child", false); // Okay since parent exists. 

    return 0; 
} 

Редактировать - на основе комментарий от Kerrek SB о том

мне может понадобиться статический член где-то

Это то, что я сделал сейчас, и я получаю поведение, которое я ожидаю.

.h

class Parent { 
private: 
    std::string myName_; // Name of this parent 
    static bool isConstructed_; 
    bool isParent_; 
protected: 
    std::string parentName_; 
public: 

    // This must be called first at least once before trying to create any children classes. 
    // The importances of this dependence has to do with the pointer of this parent being stored 
    // in a vector (outside of this class), and every child created after this will create a family that belongs to this parent. 

    explicit Parent(const std::string& name); 
    Parent(Parent &&self); 
    Parent& operator=(Parent &&transfer); 

    Parent(Parent const &) = delete; 
    Parent& operator=(Parent const &) = delete; 

    //*virtual*/ void print() { } 


    virtual void printName() const; 
    virtual void printParentName() const; 

    const std::string& getName() const; 
    const std::string& getParentName() const; 

protected: 
    // Constructor that is used when using inheritance. 
    explicit Parent(const std::string& parentName, const std::string& childName, bool isChildAParent = false); 
}; 

class Child : public Parent { 
public: 
    Child(const std::string& parentName, const std::string& childName, bool isChildAParent = false); 
}; 

.cpp

bool Parent::isConstructed_ = false; 

// Initial Constructor Must Be Called First At Least Once. 
Parent::Parent(const std::string& parentName) : 
myName_(parentName) { 
    isConstructed_ = true; 
    isParent_ = true; 
} 

// Protected Constructor Used By Child Classes. 
Parent::Parent(const std::string& parentName, const std::string& childName, bool isChildAParent) { 
    // First check to see if this child will be a parent itself 
    if (!isConstructed_) { 
     std::cout << "There must be at least 1 instance of a Parent\n" 
        << "before constructing a child.\n"; 
    } else { 
     myName_ = childName; 
     parentName_ = parentName; 
     isParent_ = isChildAParent; 
    } 
} 

// Move Constructor 
Parent::Parent(Parent&& self) { 
} 

// Move Opeartor 
Parent& Parent::operator=(Parent&& self) { 
    if (this != &self) { 
    } 
    return *this; 
} 

void Parent::printName() const { 
    std::cout << myName_ << std::endl; 
} 

void Parent::printParentName() const { 
    std::cout << parentName_ << std::endl; 
} 

const std::string& Parent::getName() const { 
    return myName_; 
} 

const std::string& Parent::getParentName() const { 
    return parentName_; 
} 

Child::Child(const std::string& parentName, const std::string& childName, bool isChildAParent) : 
Parent(parentName, childName, isChildAParent) { 
} 

main.cpp - первая версия

int main() { 

    Parent p("someParent"); 
    Child c("someParent", "someChild"); 

    // Child's Construction is successful because Parent p exists   

    return 0; 
} 

main.cpp - версия 2

int main() { 
    Child c("someParent", "someChild"); 

    // Still compiles and prints out the message that a parent needs to exists first 
    // This can be thrown as an exception to prevent the creation 
    // of a derived type without at least having a base type that already exists. 

    return 0; 
} 

Спасибо Kerrek SB.

EDIT - Подумав об отношениях, что мне нужно между Parent и Child классом, я думаю, что я пришел к выводу, что было бы лучше, чтобы иметь еще один класс, который будет Abstract Base Class что и Parent и Child типы наследуют независимо друг от друга. Таким образом, мой Manager или Storage класс, который имеет контейнер из них будет принимать shared_ptr<base_class> так, что он может контейнер любого типа, то эти наследственные классы Parent & Child может содержать список references to pointers из тех, что они связаны. Тогда будет отвечать классили Storage, чтобы проверить, действительно ли самая первая запись в его контейнере является типом Parent, а не Child. Я пошел вперед и добавил это к первоначальному вопросу о будущих ссылках на другие. Мне все равно хотелось бы вернуться к приведенной выше конструкции, о которой я только что упомянул.

+1

Возможно, вам понадобится статический член. –

+5

Это похоже на [XY Problem] (http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) для меня, что вы на самом деле пытаетесь решить? – Rakete1111

+4

Если вы меняете конструктор 'Child', требуется ссылка на' Parent', вы можете быть уверены, что существует 'Parent' –

ответ

0

Дайте базовый родительский класс статический целочисленный объект, который увеличивается в конструкторе:

class Parent{ 
    public: 
     Parent(){instances++;} 
     ~Parent(){instances--;} 
     unsigned int instances(){return instances;} 
    private: 
     static unsigned int instances = 0; 

Затем бросить исключение в классе детей, если экземпляры = 0:

class Child : public Parent{ 
    public: 
     Child(){if(Parent::instances() == 0){throw "message"}; 

По this wiki entry, используя throw в конструкторе класса - это стандартный способ предотвращения создания объекта, как для распределения стека, так и для кучи. Объект-член static в Parent используется только для поддержания текущего подсчета общего количества экземпляров.

+0

Мне не нужно знать, сколько или отслеживать, мне просто нужно знать, существует ли хотя бы один экземпляр родителя. Но спасибо за эту попытку. –

+0

Это вполне адекватный метод достижения поставленной вами цели. –

+0

Да, но только с одним статическим булевым. Если был вызван публичный конструктор родителя, флаг имеет значение true. Затем, если вызывается тип Derived, который использует защищенный конструктор типа Base, он проверяет статический bool, чтобы узнать, существует ли хотя бы один экземпляр. –