Ответы пользователя по тегу ООП
  • Почему не работает метод clone для класса Test1?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Типом результата Test::clone является std::shared_ptr<Test>.
    Строчка Test1 asd = v[1]->clone(); эквивалентна строчке Test1 asd = std::shared_ptr<Test>{ ... };.
    Оператора или конструктора преобразования из std::shared_ptr<Test> у типа Test1 нет. Трансляцию строчка Test1 asd = v[1]->clone(); не пройдет.

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

    Когда ты заранее знаешь тип, тебе незачем пользоваться клонированием, потому что ты можешь просто скопировать стандартным способом.
    Правильно твоя строчка должна выглядеть так: std::shared_ptr<Test> asd = v[1]->clone();.
    Или так:
    Test1 asd{ *std::static_pointer_cast<Test1>( v[1] ) };
    .
    Ответ написан
    Комментировать
  • Как лучше реализовать архитектуру MVC/MVP?

    @MarkusD
    все время мелю чепуху :)
    Первым делом стоит обратиться к описанию[T] MVP от Мартина Фаулера.

    Фаулер сразу оперирует поверх GUI на базе модели форм и элементов, т.е. рассматривает твой конкретный случай.
    Модель форм и элементов оперирует событиями элементов для интерпретации пользовательского ввода. Согласно Фаулеру, обработку этих событий стоит передать презентеру. Презентер в этом случае обрабатывает пользовательский ввод, передает его модели и собирает с модели обновленные данные для передачи в вид. Внутренние данные вида, которые не предназначены для модели, должен обрабатывать тоже презентер, после чего все так же передавать в вид.
    Вид должен только отображать данные модели и отсылать в презентер сигналы пользовательского ввода.

    Что будет если сделать по-другому. Например, как описано у тебя в вопросе. Сейчас у себя ты потерял гибкость и буквально разрушил абстракцию презентера. Если презентер вдруг потребуется сделать композитным (например, выбирать видимые и доступные для ввода элементы вида), ты упрешься в рефакторинг и вида, и презентера. Это отсрочит реализацию композитности логики презентера. Если потребуется держать одновременно несколько видов для одной модели, тебе придется как-то внедрять Change Propagation. В рядовом MVP за это отвечает презентер, а у тебя выйдет что вид должен подписываться на уведомление, что характерно уже не для MVP, а для MVC.

    MVP, как и MVC, является архитектурным шаблоном. Такие шаблоны находятся на самом верхнем уровне пирамиды отношений шаблонов. Это говорит о том, что уже просто реализация MVC/MVP в лоб в коде является нежелательной. MVC/MVP задают для кода UI строгое разделение по функциональности: ввод данных, процессинг и вывод данных. Вот что должно явно присутствовать в твоем коде, вот что стоит реализовать с использованием шаблонов дизайна и идиом разработки. Например, презентер или контроллер может быть сформирован на базе Rx и быть полностью децентрализованным, но при этом качественно выполнять свои функции. А вид и вовсе может быть data-driven объектом, т.е. не иметь даже минимальной личной логики.
    Каждый архитектурный шаблон, помимо легкой поддержки, сформирован из расчета на изначальную простоту, возможность стыковки с другими архитектурными шаблонами и потенциальную расширяемость.
    В результате, заложив изначально слабую реализацию MVP, дальнейшими действиями ты рискуешь размыть границы элементов архитектуры, снизить прозрачность реализации для понимания другими людьми и усложнить поддержку этого кода.
    Ответ написан
    Комментировать
  • Как сделать "переопределение" функции в c++?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Твой вопрос можно решить таким способом.
    #include <iostream>
    #include <functional>
    
    class Enemy final
    {
    public:
    	std::function<void()> update = []() {};
    };
    
    int main( int argc, char* argv[] )
    {
    	Enemy enemy;
    	enemy.update = []() { std::cout << "Hello"; };
    
    	enemy.update();
    	return 0;
    }
    Ответ написан
    Комментировать
  • Как реализовать наследование статического поля/метода, если это возможно?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    CodeInside , в твоем вопросе тебе поможет такой примитив проектирования, как Абстрактная Фабрика.

    Простая реализация на шаблоне и C++11 выглядит примерно так.
    spoiler
    template< typename TInterface, typename... TArguments >
    class AbstractFactory final
    {
    public:
    	// Produce the implementation, but return the pointer to interface.
    	inline std::shared_ptr<TInterface> Produce( const std::string& implementation_name, TArguments... arguments )
    	{
    		auto found_function = m_factory_functions.find( implementation_name );
    		return ( found_function == m_factory_functions.end() )? std::shared_ptr<TInterface>{} : found_function->second( std::forward<TArguments>( arguments )... );
    	};
    	
    	// Define the implementation.
    	template< typename TImplementation >
    	inline const bool DefineImplementation()
    	{
    		return DefineImplementation<TImplementation>( TImplementation::ClassName() );
    	};
    	
    	// Define the implementation.
    	template< typename TImplementation >
    	inline const bool DefineImplementation( const std::string& implementation_name )
    	{
    		// Abort the incorrect registration.
    		static_assert( std::is_base_of<TInterface, TImplementation>::value, "Implementation may only be derived from interface of Factory." );
    		
    		auto found_function = m_factory_functions.find( implementation_name );
    		if( found_function == m_factory_functions.end() )
    		{
    			m_factory_functions[ implementation_name ] = &AbstractFactory<TInterface, TArguments...>::template ConstructImplementation<TImplementation>;
    			return true;
    		};
    		
    		return false;
    	};
    	
    	// Check the implementation name is already defined.
    	inline const bool IsImplementationDefined( const std::string& implementation_name ) const
    	{
    		return m_factory_functions.find( implementation_name ) != m_factory_functions.end();
    	};
    	
    private:
    	// The factory function just produce implementation.
    	template< typename TImplementation >
    	static std::shared_ptr<TInterface> ConstructImplementation( TArguments... arguments )
    	{
    		return std::static_pointer_cast<TInterface>(
    			std::make_shared<TImplementation>( std::forward<TArguments>( arguments )... )
    		);
    	};
    
    private:
    	// Factory function produces the implementations of TInterface.
    	using FactoryFunction	= std::shared_ptr<TInterface> (*)( TArguments... arguments );
    	
    	std::unordered_map<std::string, FactoryFunction>	m_factory_functions;
    };


    Работает она примерно так:
    cpp.sh/93obm
    Ответ написан
    Комментировать
  • Можно ли так инициировать компоненты класса?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    en.cppreference.com/w/cpp/language/class
    Параграф "Member specification".
    Если ты используешь стандарт c++11 и выше, то инициализация полей непреложна.

    Единственным исключением будет список инициализации полей в пользовательском конструкторе.
    en.cppreference.com/w/cpp/language/initializer_list
    Ответ написан
    Комментировать
  • Как решить проблему с ООП?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Да вы, батенька, король контента!

    Эт шутка. Хоть и не смешная для самоделкиных из области геймдева. :)
    Так, а кроме шуток. Не надо вообще так делать. Тип наносимого урона/снаряда/юнита/орудия никогда не должен описываться его собственным классом. Все юниты должны быть одного класса, все пули - тоже, все здания - тоже.
    У тебя должен быть только набор общих классов для сущностей. Конкретные параметры (что откуда вылетает, как дамажит и как это все визуально выглядит) должны лежать в ресурсах.
    Рекомендую почитать: habrahabr.ru/post/255561 Изложенное куда более важно для области любимых самокатов на С++.

    Хотя... Если тебе так надо реализовать на классах, а не на ресурсах, то тебе надо просто организовать фактическую связь между сущностями. Давай предложу один страшный вариант... Дебажить это все чур самому.
    class TowerParent
    {
    public:
    	// Это тип для той штуки, которая будет создавать нам нужную боеголовку.
    	typedef FireParent* (*FireConstructor)( float, float, int, float, float, float );
    	
    	TowerParent(int _col, int _row, float _posX, float _posY, float _attackRange, float _angle);
    
    	//...
    	
    	FireParent* towerFire;
    protected:
    	FireConstructor	fire_constructor; // инстанция конструктора боеголовок.
    	
    	// Вот так мы будем определять тип конструируемой боеголовки.
    	template< class fire_t >
    	inline void DefineFireType(){
    		fire_constructor = TowerParent::template MakeFire<fire_t>;
    	};
    	
    private:
    	// А вот так мы будем конструировать боеголовку.
    	template< class fire_t >
    	static FireParent* MakeFire( float _x, float _y, int _speed, float _angle, float _targetX, float _targetY ){
    		return new fire_t( _x, _y, _speed, _angle, _targetX, _targetY );
    	};
    };
    
    
    SimpleTower( int _col, int _row, float _posX, float _posY, float _attackRange, float _angle ){
    	DefineFireType<Bullet>();
    };
    
    RocketTower(int _col, int _row, float _posX, float _posY, float _attackRange, float _angle){
    	DefineFireType<Rocket>();
    };


    Конструирование делается через:
    towerFire = fire_constructor( .... );

    Еще одна поправка. Не Rocket, а Missile. Потому что ... :)

    UPD:
    Еще один вариант, уже менее страшный - это шаблонизация базового класса:
    template< class fire_t >
    class TowerParent
    {
    public:
    	TowerParent(int _col, int _row, float _posX, float _posY, float _attackRange, float _angle);
    
    	//...
    
    	fire_t* towerFire;
    
    protected:
    	inline fire_t* MkeFire( float _x, float _y, int _speed, float _angle, float _targetX, float _targetY ){
    		return new fire_t( _x, _y, _speed, _angle, _targetX, _targetY );
    	};
    };
    
    class Bullet : public FireParent<Bullet>
    {
    public:
    	Bullet(float _x, float _y, int _speed, float _angle, float _targetX, float _targetY);
    private:
    };
    
    class Rocket : public FireParent<Rocket>
    {
    public:
    	Rocket(float _x, float _y, int _speed, float _angle, float _targetX, float _targetY);
    private:
    };
    Ответ написан
    1 комментарий
  • Можно ли перегружать базовые типы в С++?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Немного расширю ответ MiiNiPaa.

    Добавить поддержку шаблонных компараторов можно таким образом:
    template< typename item_t, template< typename > class item_traits >
    class item_processor{
    private:
    	typedef item_traits<item_t> traits_t;
    	...
    };


    Изюмина во втором параметре шаблона - это шаблонный параметр шаблона. :)
    У Девида Вандервуда по этому поводу было даже так написано: "Поскольку параметр шаблонного параметра шаблона не используется, его имя можно опустить".
    Такая запись говорит о том, что при инстанцировании или специализации шаблона в его объявлении ожидается только имя шаблона, а не его инстанцирование.

    Используется это дело вот так:
    template< typename target_t >
    class traits_less;
    
    ...
    
    item_processor<char, traits_less> char_processor;


    Все компараторы STL имеют один параметр шаблона. Таким образом, их использование тоже становится допустимо в качестве параметра для item_processor. В то же время, и свой компаратор в такую систему добавить проблемы не составит.
    Ответ написан
    Комментировать