没有默认构造函数的对象数组初始化

Dan*_*dox 60 c++ arrays constructor

#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;
}    
Run Code Online (Sandbox Code Playgroud)

我想创建一个汽车阵列,但我收到以下错误:

cartest.cpp: In function ‘int main()’:
cartest.cpp:5: error: ‘Car::Car()’ is private
cartest.cpp:21: error: within this context
Run Code Online (Sandbox Code Playgroud)

有没有办法在不使Car()构造函数公开的情况下进行初始化?

Cha*_*han 62

您可以像这样使用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;
}
Run Code Online (Sandbox Code Playgroud)

更有效的C++参考 - Scott Meyers:
第4项 - 避免使用无偿的默认构造函数

  • @miniBill:因为它是按正向顺序构建的.构造的最后一件事应该是破坏的第一件事. (7认同)
  • 如何才能使此解决方案异常安全?即,如果Car的构造函数为某些i抛出异常怎么办?我想一个人必须抓住异常,以相反的顺序解构所有已构建的Car并重新抛出...... (5认同)
  • 会发生什么,如果我只是删除[] ptr;`结尾? (4认同)
  • 确保由malloc和operator new返回的内存位置适当对齐,以便可以将其转换为任何完整对象的指针并作为对象数组进行访问。换句话说,这里没有对齐问题。 (2认同)

GMa*_*ckG 42

不.

但是,瞧!如果您使用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
} 
Run Code Online (Sandbox Code Playgroud)

附加功能:

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());
} 
Run Code Online (Sandbox Code Playgroud)

注意printCarNumbers确实应该以不同的方式设计,以接受表示范围的两个迭代器.

  • 嘿,来吧我们现在有c ++ 11而不是`&mycars [0]`让我们使用等同的`mycars.data()`,这样更可读. (7认同)
  • 我不是专家,但是,-1 表示“永远不要使用 new[]”。它的存在是有原因的。 (5认同)
  • @Dan:您可以使用`&mycars [0]`来获取指向底层数组的指针.虽然这通常仅用于将其传递给不使用`std :: vector`的遗留代码,但如果可以,则应更新该要求. (4认同)
  • 这实际上满足了我的需要,但使用矢量不是一种选择。它必须是 Car 因为我有另一种方法需要 *Car 而不是 std::vector&lt;Car&gt; (3认同)

Squ*_*all 20

您可以创建一个指针数组.

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;
Run Code Online (Sandbox Code Playgroud)

要么

Car()构造函数不需要是公共的.向构建数组的类添加静态方法:

static Car* makeArray(int length){
    return new Car[length];
}
Run Code Online (Sandbox Code Playgroud)


rus*_*tyx 7

在 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
  }
Run Code Online (Sandbox Code Playgroud)

瞧!

Car() 默认构造函数从未被调用。

mycars超出范围时,删除将自动发生。


Cod*_*ith 5

好问题。我有同样的问题,在这里找到了。真正的答案是,@ Dan-Paradox,没有标准的语法方法。因此,所有这些答案都是解决问题的多种选择。

我自己阅读了答案,但并没有特别找到适合我个人惯例的答案。我可能会坚持使用的set方法是使用默认构造函数和方法:

类MyClass
{
  int x,y,z;
上市:
  MyClass():x(0),y(0),z(0){}
  MyClass(int _x,int _y,int _z):x(_x),y(_y),z(_z){} //用于单个声明
  无效集(int _x,int _y,int _z)
  {
    x = _x;
    y = _y;
    z = _z;
  }
};

标准的初始化构造函数仍然存在,因此,如果我不需要多个初始化构造函数,我仍然可以正常对其进行初始化,但是如果不是这样,我可以使用一种set方法来设置在构造函数中初始化的所有变量。因此我可以做这样的事情:

int len = 25;
MyClass列表= new MyClass [len];
for(int i = 0; i <len; i ++)
  list [i] .set(1,2,3);

这可以正常工作并且自然地流动,而不会使代码看起来令人困惑。


对于那些想知道如何声明需要初始化的对象数组的人来说,这就是我的答案。

具体来说,您正在尝试提供一系列汽车标识,我想您应该一直保持独特。您可以使用上面说明的方法来执行此操作,然后在for循环i+1中将其用作发送给该set方法的参数-但是从我在注释中所读取的内容来看,您似乎希望ID在内部进行更多初始化,因此通过默认情况下,即使其他人使用您的班级,每个Car也具有唯一的ID Car

如果这是您想要的,则可以使用静态成员:

类车
{
  静态整数current_id;
  int id;
上市:
  Car():id(current_id ++){}

  int getId(){返回ID;}
};
int Car :: current_id = 1;

...

整车= 10;
Car * carlist =新车[cars];

for(int i = 0; i <汽车; i ++)
  cout << carlist [i] .getId()<<“”; //打印“ 1 2 3 4 5 6 7 8 9 10”

这样,由于身份是在内部进行管理的,因此您不必担心启动身份。

  • 您通过为身份循环计数器使用静态存储使您的类成为非线程安全的。如果它们只需要在每个集合中都是唯一的,那么为了效率,为循环使用局部变量更有意义。但是,在 C++ 中可能没有一种干净的方式来表达它,尤其是。不是隐藏在构造函数中的增量。 (2认同)

Rau*_*una 5

没有人评论过使用分配器来完成此任务的可能性。

#include <iostream>
#include <memory>

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++)
        (cars+i)->printNo();
}

int main()
{
  int userInput = 10;

  std::allocator<Car> carAllocator;

  // reserves space in memory for 10 car objects, but not construct them
  Car *myCars = carAllocator.allocate(10); 
  Car *myCarsBegin = myCars; // begin of array myCars

  for(int i =0; i < userInput; i++ ){
      // effectively creates the class "Car" and initializes it
      // myCars now points to the first car created
      carAllocator.construct( myCars, i );
      ++myCars;    
  }
  
  printCarNumbers(myCarsBegin,userInput);

  // destroy the objects created
  for( Car *carIterator = myCarsBegin; carIterator != myCars; ++carIterator )
      carAllocator.destroy( carIterator );

  return 0;
}
Run Code Online (Sandbox Code Playgroud)