CodeInside
@CodeInside

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

Пишу эмулятор терминала Linux (типа Bash) для курсовой. Получается я создал абстрактный базовый класс Command.
class Command
{
public:
	Command(const std::string&);

	std::vector<std::string> getArguments();
	std::vector<std::string> getOptions();

	virtual void execute(Directory& currentDirectory) = 0;
	virtual  std::string getCommandName() = 0;

protected:
	std::vector<std::string> arguments_;
	std::vector<std::string> options_;

	void parseStr(std::string);
};

От него наследуются уже рабочие комманды, которые реализуют метод execute и как-то должны реализовать своё название (пример: cp, cd, ls, pwd). Проблема в том, что не знаю как грамотно реализовать то, что у каждой комманды должно быть своё название. Оно естественно должно быть статическим. Думаю, что в C#/Java это реализуется через интерфейсы, но ведь в С++ их нету. Возможно вы уже заметили, я решил реализовать это через метод getCommandName, которые производные классы обязаны реализовать. Но в таком случае для того, чтобы получить название комманды нужно обязательно создать его объект, ибо виртуальный метод не может быть статическим. Мне это нужно для того, чтобы реализовать такой алгоритм:
  1. Считываю введённую пользователем строку.
  2. Делаю split по пробелам (учитываю скобки и т.д.) и за счёт этого получаю название комманды.
  3. Создаю указатель на Command (базовый абстрактный класс).
  4. Закидываю названия комманд в комманд в некий контейнер (стек, вектор пофиг, но реализую через вектор). Здесь и возникла эта проблема, ибо не охота создавать экземпляр каждого класса.
  5. Сравниваю полученное название комманды с названиями в классах.
  6. Создаю объект соответствующего класса и записываю его адресс в указатель на Command.
  7. Получается такая красивая запись, ради которой столько мучаюсь command->execute(/* Current directory.*/)

Что посоветуете?
  • Вопрос задан
  • 284 просмотра
Решения вопроса 2
@Mercury13
Программист на «си с крестами» и не только
Первое. Объясни, для себя и для меня, что собой представляет объект Command?

Моё видение — разделить объекты Command (введённая пользователем и разобранная строка) и Program (программа, реализующая команду). Также я нарисовал — хочешь, используй, хочешь, нет — объект Console (консоль ввода-вывода) и System (окружение программы вроде текущего каталога, переменных окружения, файловой системы).

Я тут работаю со значениями и указателями, в терминах C++03, но, возможно, вас заинтересуют умные указатели C++11.

std::string commandLine = console.getSomeCommandLine();
Command command;
std::string error;
if (!command.parse(commandLine, error)) {
  console.err().writeln(error);
  return;
}
Program* program = system.findProgram(command.programName);
if (!program) {
  console.err().writeln("Bad command or file name");
  return;
}
Console redirectedConsole = console.redirectStreams(command);
program->exec(redirectedConsole, system, command.getArguments());


Второе. Возвращай ссылки, это быстрее.
const std::vector<std::string>& getArguments() const;
const std::vector<std::string>& getOptions() const;
Ответ написан
@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
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы