Это плохой дизайн RAII?

Я имею опыт работы с Java, но позже изучил C ++ и программирую на нем уже несколько лет (в основном это отладка и написание исправлений, а не разработка программ с нуля). Тем не менее, я столкнулся с проблемой сегодня и, честно говоря, я немного удивлен, что потребовалось так много времени, чтобы найти ее.

Допустим, у меня есть класс с именем Class1, чей заголовочный файл содержит (среди прочего код):

class Class1 {
private:
Class2 object;
}

Класс Class2 не имеет указанного конструктора по умолчанию. Теперь в конструкторе Class1 я читаю двоичный заголовок файла и использую информацию, которую я анализирую, чтобы инициализировать Class2, как показано с помощью псевдокода ниже:

Class1::Class1(std::string) {
// Read some binary info from a file here

// Parse that binary info

object2 = Class2(info);

В Java, поскольку она не использует парадигму RAII, это было бы совершенно законно. Однако, так как C ++ использует RAII, объект 2 уже был инициализирован с его конструктором по умолчанию к тому времени, когда я делаю object2 = Class2(info);, Я не мог просто вызвать этот конструктор изначально (в заголовочном файле Class1), потому что у меня не было информации, необходимой для создания object еще. Тем не менее, я не могу просто сделать object2 локально по отношению к конструктору, потому что мне нужны другие функции, чтобы видеть / использовать его.

Очевидно, это не работает. Какой стандартный подход для этого материала? На самом деле я думал о том, чтобы просто изменить Class1 на указатель Class2 следующим образом:

class Class1 {
private:
Class2* objectPointer;
}

а потом звонит *objectPointer = Class2(info), Тем не менее, «Class2» в моем случае является ifstream, и кажется, что operator= Функция была удалена и не работает ни с одним из подходов.

Итак … как мне это сделать?

4

Решение

потому что ваш object не является const, это все совершенно законно. Однако, если вы хотите инициализировать objects на этапе инициализации вы должны предоставить информацию. Вы можете сделать это таким

 Class1::Class1(std::string file_name) : object(InfoFromFile(file_name)) {}

где InfoFromFile() будет либо автономной функцией (объявленной в анонимном пространстве имен в файле .cc), либо статической функцией-членом Class1, Если для генерирования информации, необходимой для Class2Вы можете предоставить его этой функции.

6

Другие решения

Если для части «чтение и анализ» не требуется, чтобы объект был сконструирован, вы можете переместить его в статическую (или не являющуюся членом) функцию и инициализировать член, используя результат этого:

Class1::Class1(std::string) :
object2(read_class2_info(some_file))
{}

Если вы действительно не можете отделить чтение файла от конструкции объекта по какой-либо причине и не можете переназначить object2 позже вам нужно будет использовать указатель. Чтобы создать объект, вы бы использовали new:

objectPointer = new Class2(info);

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

std::unique_ptr<Class2> objectPointer;

objectPointer.reset(new Class2(info));
5

Я предлагаю использовать локальную функцию для выполнения битов, которые читают двоичную информацию и непосредственно инициализируются в списке конструкторов:

namespace {

InfoObject readInfo(std::string s)
{
// Read some binary info from a file here

// Parse that binary info
return info;
}

}

Class1::Class1(std::string s)
: object(readInfo(s))
{
}

Использование указателя, конечно, также вариант. По этой же причине это более естественно работает в Java (каждый тип пользователя является внутренним указателем). Вы, вероятно, хотите использовать смарт-указатель, хотя.

2

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

Что-то вроде этого:

Class1(std::string str)
: object("some valid-but-meaningless data")
{
// parse info
object = Class2(info);

Если это не очень хорошо, вам, вероятно, придется выполнить синтаксический анализ в методе статического фактора, который затем передает информацию конструктору (который вы вполне можете сделать закрытым). Что-то вроде этого:

Class1(Info info)
: object(info)
{
// whatever else you want
}

static Class1 create(std::string)
{
// parse info
return Class1(info);
}

РЕДАКТИРОВАТЬ: Мне на самом деле нравится ответ Уолтера; использование функции при инициализации — хорошая идея. Я просто оставляю это здесь для некоторых альтернативных идей, которые вы можете рассмотреть.

1
По вопросам рекламы [email protected]