Подтвердить что ты не робот

Инициализация массива объектов без конструктора по умолчанию

#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?

4b9b3361

Ответ 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 &lt 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)
    };
}