Интересная схема внедрения зависимостей C ++ - PullRequest
0 голосов
/ 31 декабря 2018

Справочная информация: Я новичок в C ++.У меня есть проект на C #, который я хочу преобразовать в C ++, чтобы получить некоторый опыт написания полезного кода на C ++, помимо примеров из учебников.С этой целью я изучил код некоторых проектов с открытым исходным кодом, чтобы почувствовать настоящий C ++, а также попытался провести некоторый рефакторинг в довольно старом проекте с некоторым действием valgrind и некоторой статистикой adhoc в отладочной сборке, чтобы подтвердить правильность (всес реальным вкладом).(Я делал проект колледжа на C ++ несколько лет назад, но я не считаю это реальным опытом).

Актуальный вопрос:

Проект C #, который я пытаюсь преобразовать в C ++, использует инъекцию облегченной зависимости от веса.Каждый класс получает объект «context» в конструкторе.Сначала он вставляется в «контекст», а затем запрашивает свои собственные зависимости, чтобы циклические зависимости не были проблемой.

После некоторых размышлений я придумал следующую схему в C ++.Каждый объект DI получает свои зависимости по ссылке в конструкторе.Я делаю все поля объектов DI в одном классе (по значению как подобъекты) и связываю их в списке инициализации.

// Yes, the project is compiler for a toy programming language
class Compiler {
    Logger log;
    Options options;
    ParserDriver parserDriver;
    DeclarationAnalysis declarationAnalysis;
    CodeTypeAnalysis codeTypeAnalysis;
    FlowAnalysis flowAnalysis;
    CodeGeneration codeGeneration;
    Check check;
    Typings typings;
    Operators operators;
    Symtab symtab;
public:
    Compiler();
};

Compiler::Compiler() :
    log(),
    options(),
    parserDriver(log),
    declarationAnalysis(log, symtab, check),
    codeTypeAnalysis(log, symtab, operators, typings, check),
    flowAnalysis(log),
    codeGeneration(typings, symtab),
    check(log, symtab),
    typings(symtab),
    operators(symtab, log, typings),
    symtab()
{}

// Example DI object
class DeclarationAnalysis {
    Logger* log;
    Symtab* symtab;
    Check* check;
public:
    DeclarationAnalysis(Logger&, Symtab&, Check&);
}

DeclarationAnalysis::DeclarationAnalysis(Logger& log, Symtab& symtab, Check& chack) 
    : log(&log), symtab(&symtab), check(&check) {}

Является ли такой код правильным в том смысле, что он безопасен, без неопределенного поведения(например, symtab инициализируется последним, но передается в качестве аргумента конструкторам других полей)?Это кажется очень элегантным с первого взгляда.Память упакована, даже может быть выделена в стеке.Я заново открыл существующий шаблон?

1 Ответ

0 голосов
/ 31 декабря 2018

Списки инициализации конструктора вызываются в том порядке, в котором объекты существуют в классе.Для компиляторов можно установить значение warn.if, если список конструкторов имеет другой порядок.Включите это.

Вы можете взять ссылки на неинициализированные значения и передать их.Все, кроме хранения указателя или ссылки на эти неинициализированные объекты, не является хорошим планом.

В вашем примере кода вы храните только указатели.Это безопасно.

Деструкторы этих типов не могут предполагать, что указанные объекты существуют, поскольку в процессе строительства Compiler он может потерпеть неудачу (через исключение) и привести к разрушению уже созданных подобъектов.Это трудно понять.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...