Инициализация массива объектов без конструктора по умолчанию
#include <iostream>
class Car
{
private:
Car(){};
int _no;
public:
Car(int no)
{
_no=no;
}
void printNo()
{
std::cout<<_no<<std::endl;
}
};
void printCarNumbers(Car *cars, int length)
{
for(int i = 0; i<length;i++)
std::cout<<cars[i].printNo();
}
int main()
{
int userInput = 10;
Car *mycars = new Car[userInput];
for(int i =0;i < userInput;i++)
mycars[i]=new Car[i+1];
printCarNumbers(mycars,userInput);
return 0;
}
Я хочу создать массив автомобилей, но я получаю следующую ошибку:
cartest.cpp: In function ‘int main()’:
cartest.cpp:5: error: ‘Car::Car()’ is private
cartest.cpp:21: error: within this context
Есть ли способ сделать эту инициализацию, не создав конструктор Car() public?
Ответы
Ответ 1
Неа.
Но вот! Если вы используете std::vector<Car>
, как и должно быть (никогда не используйте new[]
), вы можете точно указать, как должны быть построены элементы *.
* Хорошо. Вы можете указать значение для создания копий.
Вот так:
#include <iostream>
#include <vector>
class Car
{
private:
Car(); // if you don't use it, you can just declare it to make it private
int _no;
public:
Car(int no) :
_no(no)
{
// use an initialization list to initialize members,
// not the constructor body to assign them
}
void printNo()
{
// use whitespace, itmakesthingseasiertoread
std::cout << _no << std::endl;
}
};
int main()
{
int userInput = 10;
// first method: userInput copies of Car(5)
std::vector<Car> mycars(userInput, Car(5));
// second method:
std::vector<Car> mycars; // empty
mycars.reserve(userInput); // optional: reserve the memory upfront
for (int i = 0; i < userInput; ++i)
mycars.push_back(Car(i)); // ith element is a copy of this
// return 0 is implicit on main with no return statement,
// useful for snippets and short code samples
}
С дополнительной функцией:
void printCarNumbers(Car *cars, int length)
{
for(int i = 0; i < length; i++) // whitespace! :)
std::cout << cars[i].printNo();
}
int main()
{
// ...
printCarNumbers(&mycars[0], mycars.size());
}
Примечание printCarNumbers
действительно должно быть спроектировано по-разному, чтобы принять два итератора, обозначающих диапазон.
Ответ 2
Вы можете использовать новое размещение следующим образом:
class Car {
int _no;
public:
Car( int no ) :_no( no ) {
}
};
int main() {
void* raw_memory = operator new[]( NUM_CARS * sizeof( Car ) );
Car* ptr = static_cast<Car*>( raw_memory );
for( int i = 0; i < NUM_CARS; ++i ) {
new( &ptr[i] )Car( i );
}
// destruct in inverse order
for( int i = NUM_CARS - 1; i >= 0; --i ) {
ptr[i].~Car();
}
operator delete[]( raw_memory );
return 0;
}
Ссылка на более эффективный С++ - Скотт Майерс:
Пункт 4 - Избегайте бесполезных конструкторов по умолчанию
Ответ 3
Вы можете создать массив указателей.
Car** mycars = new Car*[userInput];
for (int i=0; i<userInput; i++){
mycars[i] = new Car(...);
}
...
for (int i=0; i<userInput; i++){
delete mycars[i];
}
delete [] mycars;
или
Конструктор Car() не должен быть общедоступным. Добавьте статический метод в ваш класс, который создает массив:
static Car* makeArray(int length){
return new Car[length];
}
Ответ 4
Нет, нет. Новое выражение допускает инициализацию по умолчанию или вообще не инициализирует.
Обходным решением будет выделение необработанного буфера памяти с помощью operator new[]
, а затем построение объектов в этом буфере с помощью метода place-new с конструктором, отличным от стандартного.
Ответ 5
Хороший вопрос. У меня был тот же вопрос, и я нашел его здесь. Реальный ответ: @Dan-Paradox, нет стандартного синтаксического способа сделать это. Таким образом, все эти ответы представляют собой множество альтернатив для решения проблемы.
Я сам прочитал ответы и особо не нашел ни одного из них идеальным для моего личного соглашения. Метод, который я, вероятно, придерживаюсь, использует конструктор по умолчанию и метод set
:
class MyClass
{
int x,y,z;
public:
MyClass(): x(0), y(0), z(0) {}
MyClass(int _x,int _y,int _z): x(_x), y(_y), z(_z) {} // for single declarations
void set(int _x,int _y,int _z)
{
x=_x;
y=_y;
z=_z;
}
};
Стандартный конструктор инициализации все еще существует, поэтому я все равно могу его инициализировать, если мне не нужно больше одного, но в противном случае у меня есть метод set
, который устанавливает все переменные, которые инициализируются в конструкторе, Таким образом, я мог бы сделать что-то вроде этого:
int len=25;
MyClass list = new MyClass[len];
for(int i = 0; i < len; i++)
list[i].set(1,2,3);
Это прекрасно работает и протекает естественным образом, не делая код выглядящим запутанным.
Теперь, когда мой ответ для тех, кто задается вопросом, как объявить массив объектов, которые необходимо инициализировать.
В частности, вы пытаетесь дать массив идентификаторов автомобилей, и я бы предположил, что вы хотите всегда быть уникальными. Вы можете сделать это с помощью моего метода, который я объяснил выше, а затем в цикле for
использовать i+1
как аргумент, отправленный методу set
, но из того, что я прочитал в ваших комментариях, кажется, что вы хотите Иды более внутренне инициированы, так что по умолчанию каждый автомобиль имеет уникальный идентификатор, даже если кто-то использует ваш класс Car
.
Если это то, что вы хотите, вы можете использовать статический член:
class Car
{
static int current_id;
int id;
public:
Car(): id(current_id++) {}
int getId() { return id; }
};
int Car::current_id = 1;
...
int cars=10;
Car* carlist = new Car[cars];
for(int i = 0; i < cars; i++)
cout<<carlist[i].getId()<<" "; // prints "1 2 3 4 5 6 7 8 9 10"
Таким образом, вам не нужно беспокоиться об инициализации идентификаторов, поскольку они управляются внутри.
Ответ 6
Вы всегда можете создать массив указателей, указывая на объекты автомобилей, а затем создать объекты в цикле for, как вы хотите, и сохранить их адрес в массиве, например:
#include <iostream>
class Car
{
private:
Car(){};
int _no;
public:
Car(int no)
{
_no=no;
}
void printNo()
{
std::cout<<_no<<std::endl;
}
};
void printCarNumbers(Car *cars, int length)
{
for(int i = 0; i<length;i++)
std::cout<<cars[i].printNo();
}
int main()
{
int userInput = 10;
Car **mycars = new Car*[userInput];
int i;
for(i=0;i<userInput;i++)
mycars[i] = new Car(i+1);
отметить новый метод!!!
printCarNumbers_new(mycars,userInput);
return 0;
}
Все, что вам нужно изменить в новом методе, - это обработка автомобилей как указателей, чем статических объектов
в параметре и при вызове метода printNo()
например:
void printCarNumbers_new(Car **cars, int length)
{
for(int i = 0; i<length;i++)
std::cout<<cars[i]->printNo();
}
в конце main хорошо удалить всю динамически распределенную память, подобную этой
for(i=0;i<userInput;i++)
delete mycars[i]; //deleting one obgject
delete[] mycars; //deleting array of objects
Надеюсь, я помог, ура!
Ответ 7
В С++ 11 std::vector
вы можете создавать элементы на месте с помощью emplace_back
:
std::vector<Car> mycars;
for (int i = 0; i < userInput; ++i)
{
mycars.emplace_back(i + 1); // pass in Car() constructor arguments
}
Voila!
Невозможно вызвать конструктор по умолчанию().
Удаление будет происходить автоматически, когда mycars
выходит за рамки.
Ответ 8
Один из способов решения - предоставить статический метод factory для распределения массива, если по какой-либо причине вы хотите предоставить конструктору закрытый.
static Car* Car::CreateCarArray(int dimensions)
Но почему вы держите один конструктор открытым и другим частным?
Но в любом случае еще один способ - объявить публичный конструктор со значением по умолчанию
#define DEFAULT_CAR_INIT 0
Car::Car(int _no=DEFAULT_CAR_INIT);
Ответ 9
Я не думаю, что там безопасный метод, который может делать то, что вы хотите.
Ответ 10
Вы можете использовать оператор на месте new. Это было бы немного ужасно, и я бы рекомендовал сохранить в factory.
Car* createCars(unsigned number)
{
if (number == 0 )
return 0;
Car* cars = reinterpret_cast<Car*>(new char[sizeof(Car)* number]);
for(unsigned carId = 0;
carId != number;
++carId)
{
new(cars+carId) Car(carId);
}
return cars;
}
И определите соответствующий уничтожить, чтобы соответствовать новому, используемому в этом.
Ответ 11
Мой путь
Car * cars;
// else were
extern Car * cars;
void main()
{
// COLORS == id
cars = new Car[3] {
Car(BLUE),
Car(RED),
Car(GREEN)
};
}