2015-10-22 2 views
1

У меня есть простой пример Shape, где я могу создать Circle s или Square s.Доступ к членам производного класса из созданного на заводе экземпляра

я добавил дополнительный «contents» атрибут к Circle класса, который не является частью производного класса Square или Shape базового класса.

Проблема заключается в том, что при создании экземпляра класса Circle с использованием моей фабрики я не могу изменить contents созданного объекта.

#include <iostream> 

using namespace std; 

// Shape base clas 
class Shape { 
public: 

    // Shape constructor; 
    Shape() { 
     id_ = total_++; 
    } 

    // Virtual draw method 
    virtual void draw() = 0; 

protected: 

    int id_; 
    static int total_; 
}; 
int Shape::total_ = 0; 

// Circle derived class 
class Circle : public Shape { 
public: 
    void draw() { 
     contents = 0; 
     cout << "circle " << id_ << ": draw, contents: " << contents << endl; 
    } 

    // Attribute to attempt to access 
    int contents; 
}; 

// Square derived class 
class Square : public Shape { 
public: 
    void draw() { 
     cout << "square " << id_ << ": draw" << endl; 
    } 
}; 

// Factory class 
class Factory { 
public: 
    Shape* createCurvedInstance() { 
     return new Circle; 
    } 
    Shape* createStraightInstance() { 
     return new Square; 
    } 
}; 

// Main 
int main() 
{ 
    Factory* factory = new Factory; 
    Shape* thing = factory->createCurvedInstance(); 

    // Draw method works fine (as it should) 
    thing->draw(); 

    // Fails: "expression must have class type" 
    thing.contents = 4; 

    system("pause"); 

    return 0; 
} 

Как я могу получить доступ к атрибутам производного класса, когда я создаю экземпляр этого, используя завод?

+0

Вы этого не думали. Вы возвращаете статический тип 'Shape *', и весь компилятор знает во время компиляции 'Shape *' stuff. Поскольку 'contents' не является частью' Shape', вы получаете ошибку. Кроме того, ваш объект 'Shape' должен иметь виртуальный деструктор, учитывая, как вы собираетесь его использовать. – PaulMcKenzie

ответ

0

Оказывается, я не хотел фабрики вообще!

То, что я действительно хотел, было своего рода контейнерным классом для хранения различных типов фигур. Ни одна из форм не является производной от класса ShapeContainer, но через нее можно получить доступ к обеим формам.

#include <iostream> 

using namespace std; 

// Circle class 
class Circle { 
public: 
    Circle() { 
     contents = 2; 
    } 
    void draw() { 
     cout << "circle " << contents << endl; 
    } 

    int contents; 
}; 

// Square class 
class Square { 
public: 
    void draw() { 
     cout << "square" << endl; 
    } 
}; 

// Shape containter class 
class ShapeContainer { 
public: 
    Circle* getCircle() { 
     return new Circle; 
    } 
    Square* getSquare() { 
     return new Square; 
    }; 
}; 

// Main 
int main() 
{ 
    ShapeContainer* container = new ShapeContainer; 
    Circle* circle = container->getCircle(); 

    circle->draw(); 
    circle->contents = 42; 
    circle->draw(); 

    system("pause"); 
    return 0; 
} 

Это позволяет мне сделать оба Circle с и Square сек, все еще будучи в состоянии получить доступ к содержимому в Circle объекта!

Иногда вам нужно взглянуть на вещи с совершенно новой точки зрения, чтобы получить функциональность, которую вы действительно хотите ...

2

Ни в коем случае, если вы не бросили, а Throw not Cast. Вся идея полиморфных - это случаи, когда они становятся доступными через неизменный интерфейс. Они приветствуют репликацию IS-A, где Circle - это форма для всех целей и целей, за исключением деталей реализации, которые вас никто не интересует. Если вы добавляете публичное «содержимое» к своему кругу, это уже не форма, поэтому не должны строиться на заводе.

+0

Ну, это отстой ... Мне нужен элегантный способ сделать это ... В моей ситуации у меня есть серия отдельных классов баз данных, которые получены из базового базового базового класса. Мне нужен способ создания экземпляров любого из классов базы данных (т. Е. Через использование фабрики) и доступа к полям базы данных, которые хранятся в членах, специфичных для каждого производного класса ... – tjwrona1992

+0

В принципе мне нужна фабрика, которая не return 'Shape's. Мне нужна фабрика, которая возвращает «Круг» или «Квадрат» на основе того, что требуется. – tjwrona1992

+0

Как я уже сказал, ваши дополнительные данные должны быть либо деталями реализации, и поэтому вы не пытаетесь получить доступ извне. Или, если вы не можете создать единый интерфейс, это означает, что между вашей классой и фабрикой нет отношения IS-A (это редко бывает, кстати :). – SergeyA

1

Поскольку Shape не имеет content, вы не можете изменить content от указателя Shape. полная остановка.

Однако, если вы знаете, что ваш конкретный Shape является фактически Circle и имеет content, вы можете привести к указателю Circle.

void set_content(Shape*shape, int content) 
{ 
    auto circle = dynamic_cast<Circle*>(shape); 
    if(circle) 
    circle->content = content; 
} 

Эта версия дополнительный сейф: он не предполагает, что shape является Circle*, но использует dynamic_cast<Circle*>, который будет возвращать ненулевым только если shape является фактически Circle*.

dynamic_cast<> поставляется с некоторыми расходами, которых вы можете избежать. Если у вас есть какой-либо другой дурак доказательство способ установить, что ваш shape является фактически Circle, вы можете использовать простой static_cast<>:

class Shape 
{ 
public: 
    virtual bool has_content() const { return false; } 
    // ... 
}; 

class ShapeWithContent : public Shape 
{ 
public: 
    bool has_content() const override { return true; } 
    int content = 0; 
}; 

class Circle : public ShapeWithContent 
{ 
    // ... 
}; 

void set_content(Shape*shape, int content) 
{ 
    if(shape->has_content()) 
    static_cast<ShapeWithContent*>(shape)->content = content; 
} 

Сказав все это, я хотел бы подчеркнуть, что вы должны попытаться создать свой код таким образом, чтобы такие трюки были излишними/ненужными.

+2

Полностью поражает цель завода. – SergeyA

+0

@SergeyA Это может быть так, но это был не вопрос. – Walter

+0

Я всегда чувствую себя обязанным пройти лишнюю милю. Очевидно, здесь у нас есть ученик, поэтому вместо того, чтобы показывать ему грязные трюки, мы лучше обучим его. – SergeyA

Смежные вопросы