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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4754763/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 16:22:45  来源:igfitidea点击:

Object array initialization without default constructor

c++arraysconstructor

提问by Dan Paradox

#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;
}    

I want to create a car array but I get the following error:

我想创建一个汽车数组,但出现以下错误:

cartest.cpp: In function ‘int main()':
cartest.cpp:5: error: ‘Car::Car()' is private
cartest.cpp:21: error: within this context

is there a way to make this initialization without making Car() constructor public?

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

采纳答案by GManNickG

Nope.

不。

But lo! If you use std::vector<Car>, like you should be (never ever use new[]), then you can specify exactly how elements should be constructed*.

但是!如果您使用std::vector<Car>,就像您应该使用的那样(永远不要使用new[]),那么您可以准确指定应该如何构造元素*。

*Well sort of. You can specify the value of which to make copies of.

*好吧。您可以指定要制作副本的值。



Like this:

像这样:

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

With the additional function:

使用附加功能:

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());
} 

Note printCarNumbersreally should be designed differently, to accept two iterators denoting a range.

注意printCarNumbers真的应该被设计成不同的,接受两个迭代器来表示一个范围。

回答by Chan

You can use placement-new like this:

您可以像这样使用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;
}

Reference from More Effective C++ - Scott Meyers:
Item 4 - Avoid gratuitous default constructors

参考更有效的 C++ - Scott Meyers:
第 4 条 - 避免无缘无故的默认构造函数

回答by Squall

You can create an array of pointers.

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

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;

or

或者

Car() constructor does not need to be public. Add a static method to your class that builds an array:

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

static Car* makeArray(int length){
    return new Car[length];
}

回答by Codesmith

Good question. I had the same question, and found it here. The real answer is, @Dan-Paradox, there is no standard syntactical way of doing it. So, all these answers are a variety of alternatives to get around the problem.

好问题。我有同样的问题,在这里找到了。真正的答案是,@Dan-Paradox,没有标准的语法方法可以做到这一点。因此,所有这些答案都是解决问题的各种替代方案。

I read the answers myself, and didn't particularly find any of them perfect for my personal convention. The method that I'll probably stick with is using a default constructor and a setmethod:

我自己阅读了答案,并没有发现其中任何一个特别适合我的个人惯例。我可能会坚持使用的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;
  }
};

The standard initialization constructor is still there, so I can still initialize it normally if I don't need more than one, but if otherwise, I have a setmethod which sets all the variables that are initialized in the constructor. Thus I could do something like this:

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

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

This works fine and flows naturally, without making code look confusing.

这工作正常并且自然流动,不会使代码看起来混乱。



Now that's my answer for those wondering how to declare an array of objects that need to be initialized.

现在这就是我对那些想知道如何声明需要初始化的对象数组的人的回答。

For you specifically, you're trying to give an array of cars identities, which I'd suppose you want to always be unique. You could do it with my method I explained above, and then in the forloop use i+1as the argument sent to the setmethod - but from what I've read in your comments, it seems like you want the ids more internally initiated, so that by default each Car has a unique id, even if someone else uses your class Car.

具体来说,您正在尝试提供一系列汽车标识,我想您希望这些标识始终是独一无二的。你可以用我上面解释的方法来做,然后在for循环中使用i+1作为发送给set方法的参数- 但从我在你的评论中读到的,似乎你想要更内部地启动 id,这样通过默认每个 Car 都有一个唯一的 id,即使其他人使用您的 class Car

If this is what you want, you can use a static member:

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

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"

In this way, you don't have to worry at all about initiating the identities since they are managed internally.

通过这种方式,您完全不必担心启动身份,因为它们是在内部管理的。

回答by AnT

No, there isn't. New-expression only allows default initialization or no initialization at all.

不,没有。New-expression 只允许默认初始化或根本不初始化。

The workaround would be to allocate raw memory buffer using operator new[]and then construct objects in that buffer using placement-new with non-default constructor.

解决方法是operator new[]使用具有非默认构造函数的placement-new分配原始内存缓冲区,然后在该缓冲区中构造对象。

回答by rustyx

In C++11's std::vectoryou can instantiate elements in-place using emplace_back:

在 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
  }

Voila!

瞧!

Car() default constructor never invoked.

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

Deletion will happen automatically when mycarsgoes out of scope.

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

回答by kondilidisn

You can always create an array of pointers , pointing to car objects and then create objects, in a for loop, as you want and save their address in the array , for example :

您始终可以创建一个指针数组,指向汽车对象,然后根据需要在 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);

note new method !!!

注意新方法!!!

  printCarNumbers_new(mycars,userInput);


  return 0;
}    

All you have to change in new method is handling cars as pointers than static objects in parameter and when calling method printNo() for example :

在新方法中你需要改变的只是将汽车作为指针而不是参数中的静态对象来处理,例如在调用方法 printNo() 时:

void printCarNumbers_new(Car **cars, int length)
{
    for(int i = 0; i<length;i++)
         std::cout<<cars[i]->printNo();
}

at the end of main is good to delete all dynamicly allocated memory like this

在 main 的末尾最好像这样删除所有动态分配的内存

for(i=0;i<userInput;i++)
  delete mycars[i];      //deleting one obgject
delete[] mycars;         //deleting array of objects

Hope I helped, cheers!

希望我有所帮助,干杯!

回答by Neera

One way to solve is to give a static factory method to allocate the array if for some reason you want to give constructor private.

一种解决方法是,如果出于某种原因您想将构造函数设为私有,则提供一个静态工厂方法来分配数组。

static Car*  Car::CreateCarArray(int dimensions)

But why are you keeping one constructor public and other private?

但是你为什么要让一个构造函数公开而其他的私有呢?

But anyhow one more way is to declare the public constructor with default value

但无论如何,另一种方法是使用默认值声明公共构造函数

#define DEFAULT_CAR_INIT 0
Car::Car(int _no=DEFAULT_CAR_INIT);

回答by Dagang

I don't think there's type-safe method that can do what you want.

我不认为有类型安全的方法可以做你想做的事。

回答by Mitch Pisa

My way

我的方式

Car * cars;

// else were

extern Car * cars;

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