Я имею опыт работы с 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=
Функция была удалена и не работает ни с одним из подходов.
Итак … как мне это сделать?
потому что ваш object
не является const
, это все совершенно законно. Однако, если вы хотите инициализировать objects
на этапе инициализации вы должны предоставить информацию. Вы можете сделать это таким
Class1::Class1(std::string file_name) : object(InfoFromFile(file_name)) {}
где InfoFromFile()
будет либо автономной функцией (объявленной в анонимном пространстве имен в файле .cc), либо статической функцией-членом Class1
, Если для генерирования информации, необходимой для Class2
Вы можете предоставить его этой функции.
Если для части «чтение и анализ» не требуется, чтобы объект был сконструирован, вы можете переместить его в статическую (или не являющуюся членом) функцию и инициализировать член, используя результат этого:
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));
Я предлагаю использовать локальную функцию для выполнения битов, которые читают двоичную информацию и непосредственно инициализируются в списке конструкторов:
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 (каждый тип пользователя является внутренним указателем). Вы, вероятно, хотите использовать смарт-указатель, хотя.
Я думаю, что вам придется инициализировать 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);
}
РЕДАКТИРОВАТЬ: Мне на самом деле нравится ответ Уолтера; использование функции при инициализации — хорошая идея. Я просто оставляю это здесь для некоторых альтернативных идей, которые вы можете рассмотреть.