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


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

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 &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"

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

вы всегда можете создать массив указателей, указывающих на объекты 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;
}

и определите соответствующее уничтожение, чтобы соответствовать новому, используемому в этом.

мой путь

Car * cars;

// else were

extern Car * cars;

void main()
{
    // COLORS == id
    cars = new Car[3] {
        Car(BLUE),
            Car(RED),
            Car(GREEN)
    };
}