Инициализация массива объектов без конструктора по умолчанию
#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 ()?
11 ответов:
Неа.
но Ло! Если вы используете
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's 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
действительно должны быть разработаны по-разному, чтобы принять два итератора, обозначающие диапазон.
вы можете использовать placement-new следующим образом:
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; }
ссылка из более эффективного C++ - Scott Meyers:
Пункт 4-Избегайте безвозмездных конструкторов по умолчанию
Вы можете создать массив указателей.
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]; }
нет, нет. New-expression позволяет только инициализацию по умолчанию или вообще не инициализировать.
обходным путем было бы выделить необработанный буфер памяти с помощью
operator new[]
а затем построить объекты в этом буфере с помощью placement-new с конструктором не по умолчанию.
хороший вопрос. У меня был тот же вопрос, и я нашел его здесь. Реальный ответ, @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"таким образом, вам не нужно беспокоиться о том, чтобы инициировать идентификаторы, так как они управляются внутри.
вы всегда можете создать массив указателей, указывающих на объекты car, а затем создать объекты в цикле 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
надеюсь, я помог, ура!
В C++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 }
вуаля!
конструктор по умолчанию Car () никогда не вызывается.
удаление произойдет автоматически, когда
mycars
выходит за рамки.
один из способов решения-дать статический заводской метод для выделения массива, если по какой-то причине вы хотите дать конструктор private.
static Car* Car::CreateCarArray(int dimensions)
но почему вы держите один конструктор и другие частные?
но в любом случае еще один способ-объявить открытый конструктор по умолчанию
#define DEFAULT_CAR_INIT 0 Car::Car(int _no=DEFAULT_CAR_INIT);
вы можете использовать оператор new. Это было бы немного ужасно, и я бы рекомендовал держать на заводе.
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; }
и определите соответствующее уничтожение, чтобы соответствовать новому, используемому в этом.