2013-03-31 3 views
2

Я новичок в C++, и этот вопрос, вероятно, кажется noobish, поскольку я просто больше из Java/Actionscript3, и я никогда раньше не занимался вещами, связанными с памятью.C++ Сохранение ссылки/указателя абстрактного типа

Во всяком случае, как и для меня проблема: Сцена это абстрактный класс (так что я могу извлечь из него и есть несколько сцен, как MainScene и т.д. ')

То, что я хотел SceneManager сделать, это есть указатель/ссылка на выбранную сцену (curSc) а потом я бы установить сцену так: setScene (& someScene)

Но, как я понимаю, есть указатель я также должен инициализировать его следующим образом: curSc = новый Место действия; Но это не позволит мне сделать это, как сцена является абстрактным классом ..

class Scene { 

public: 
    Scene(void){} 
    virtual ~Scene(void){} 
    virtual void update() = 0; 
    virtual void render() = 0; 
}; 


class SceneManager { 

public: 
    SceneManager(void); 
    ~SceneManager(void); 
    void setScene(Scene *sc); 
    Scene* curSc; 
} 

До сих пор мне кажется, как с помощью указателя в этом случае не так, и это не будет работать .. Но Я хотел бы знать, как достичь функциональности я пытаюсь получить здесь

Спасибо очень

Edit по запросу: , который, как я пытался использовать его:

Во-первых, я производный класс по имени GameScene и здесь:

class GameScene : public Scene 
{ 
public: 
    GameScene(void); 
    void render(); 
    void update(); 
} 

в моей основной функции() у меня есть:

GameScene gamescene; 
ScreenManager manager; 

manager.setScene(&gamescene); 
+0

'setScene (& someScene)', скорее всего, привести к ошибке! Вы берете адрес 'someScene', что означает, что' someScene' не является указателем. Теперь вопрос: где живет 'someScene'? Если это локальная переменная, она будет уничтожена, как только вы покинете область, в которой она объявлена, и указатель, хранящийся в «SceneManager», укажет на объект, который больше не существует. Правило большого пальца: не используйте адрес-оператора (и что-то), если вы точно не знаете, что вы делаете. Не используйте указатели, но читайте о умных указателях и используйте их !!! – JohnB

ответ

0

Точно. Вы не можете сделать

curSc = new Scene 

потому что оно абстрактно. Если вы действительно хотите объявить экземпляр Scene, сделайте его не абстрактным. т. е. реализовать все методы.

OR (Я думаю, это именно то, что вы намеревались сделать) создать класс, который наследует от Scene и реализует все виртуальные методы.

class View : public Scene { 

public: 
    View(){} 
    virtual ~View(void){} 
    virtual void update(); 
    virtual void render(); 
}; 

и реализовать виртуальные методы, как

void View::update() { 
// 
} 

void View::render() { 
// 
} 

Затем вы можете реализовать setScene в

void SceneManager::setScene(Scene *sc) 
{ 
    curSc = sc; 
} 

и назвать его как

SceneManager smag; 
Scene *nsc = new View; 
smag.setScene(nsc); 
+0

Я уже сделал это, но как правильно передать этот класс методу setScene? И не нужно ли мне инициализировать указатель между прочим? – Radicate

+0

Да, вам нужно. – 2013-03-31 16:15:05

+0

Я передал его в setScene и сделал curSc = sc; но затем, когда я попытался использовать curSc, он дал мне ошибки нарушения прав доступа, которые, как я думал, я получил, потому что раньше я не инициализировал указатель, но потом я не хочу инициализировать указатель на производный класс, поскольку я мог бы установить сцену для разных видов – Radicate

1

вам нужен конкретный производный класс сцены:

class MyScene: public Scene { 

public: 
    MyScene(void){} 
    virtual ~MyScene(void){} 
    virtual void update(); 
    virtual void render(); 
}; 

И пустота setScene (сцена * СБН) будет:

void setScene(Scene *sc) 
{ 
    curSc = (Scene*)sc; 
} 

sc в этом случае будет указателем MyScene. Абстрактные экземпляры классов (например, интерфейсы) не могут быть созданы, а сцена - просто интерфейс.

+2

Возможно, вы имели в виду curSc = sc; ? –

+0

Как насчет инициализации указателя? Я попытался сделать это до этого, и я не инициализировал его, после этого у меня появились ошибки в доступе. – Radicate

+0

Смотрите мой комментарий к вашему вопросу. – JohnB

0

Это не неправильно использовать указатель здесь. Но в отличие от ссылки, указатель призывает своего пользователя к учету NULL. Это предостережение. Ссылки могут также ссылаться на NULL, но добавление защиты против него приведет вас к "dark side". Таким образом, разумно предположить, что ссылка будет ссылаться на живой объект, и в этом смысле ссылки лучше, чем указатели.

Теперь, если вы на самом деле, на самом деле хотите сохранить ссылку вы можете использовать на месте строительной техники, потому что вы можете (пере) присвоить ссылку на конструктор:

class SceneManager { 

public: 
    SceneManager(Scene& sc) : curSc(sc) {} 
    ~SceneManager(void); 
    SceneManager& setScene(Scene &sc) { 
     SceneManager* cheatCompiler = new(this) SceneManager(sc); 
     return *cheatCompiler; 
    } 
    Scene& curSc; 
} 

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

Было сказано, что я по-прежнему рекомендую вам использовать указатель. Если вы не хотите постоянно проверять NULL, вы можете реализовать что-то вроде Null Object Pattern.

0

Вы должны использовать интеллектуальные указатели и не создавать переменные-члены public. Пример:

class Scene { 
public: 
    virtual ~Scene(){} 
    virtual void update() = 0; 
    virtual void render() = 0; 
}; 

class MyScene : public Scene { 
public: 
    virtual void update() { /*...*/ } 
    virtual void render() { /*...*/ } 
}; 

class SceneManager { 

public: 
    SceneManager(); 
    ~SceneManager(); 
    void setScene(const std::shared_ptr<Scene> & sc) { 
     curSc = sc; 
    } 
private: 
    std::shared_ptr<Scene> curSc; 
}; 

void someProc() { 
    std::shared_ptr<Scene> sc = std::make_shared<MyScene>(); 
    auto manager = std::make_shared<SceneManager>(); 
    manager->setScene (sc); 
    // ... 
} 

Без умных указателей, это будет выглядеть (но помните, delete объекты, созданные с new, если они больше не нужны):

class Scene { 
public: 
    virtual ~Scene(){} 
    virtual void update() = 0; 
    virtual void render() = 0; 
}; 

class MyScene : public Scene { 
public: 
    virtual void update() { /*...*/ } 
    virtual void render() { /*...*/ } 
}; 

class SceneManager { 

public: 
    SceneManager(); 
    ~SceneManager(); 
    void setScene(Scene * sc) { 
     curSc = sc; 
    } 
private: 
    Scene * curSc; 
}; 

void someProc() { 
    Scene * sc = new MyScene; 
    SceneManager * manager = new SceneManager; 
    manager->setScene (sc); 
    // ... 
} 
+0

Я вижу, спасибо за отличный информативный ответ, но дело в том, что я не хотел ограничиваться только одним классом, который выводит из сцены вы инициализировали указатель так: Scene * sc = new MyScene; но что, если у меня есть 10 типов сцен, которые я могу решить установить вместо них? – Radicate

+0

Объявите другие классы (например, 'MySecondScene') и замените' new MyScene' на 'new MySecondScene' – JohnB

+0

Да. Я пытался избежать этого;/Я хотел иметь возможность изменить его на многие типы сцен во время выполнения вместо изменения кода для этого – Radicate

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