问题 如何使用new传递构造函数参数


我试图将参数传递给构造函数,但同时制作一个像这样的对象数组。我使用以下代码到达那里:

PointPtr centroids = new Point[k](5);

好吧,这不是语法错误,但它没有编译。我真的不想将“5”硬编码为Point的默认构造函数。对我应该怎么做有什么想法?谢谢!

顺便说一句,我做到了 typedef Point *PointPtr 其他地方已经。

对不起,如果标题不准确。我不知道如何总结这一点。


10115
2017-11-29 23:07


起源

对不起,你不能用动态数组做到这一点。考虑使用矢量,让我们使用 emplace_back 构造对象。 - Neil Kirk
还有另一个要避免的理由 new 。 - M.M
它总是“尚未”......为什么不先教它们? - Neil Kirk
这就是学校陷入困境的时候。在我的学校,他们不教 std::vector 甚至 std::string。这就是为什么我总是跳过课...... - Blazo
but here's another question  - 不,如果您有更多问题,请单独询问。一个帖子=一个问题。 - szczurcio


答案:


你可以创建一个指针数组(即 Point**)并分两步初始化它们:

  1. 创建数组:

    PointPtr* centroids = new PointPtr[k];

  2. 初始化:

    for (int i=0 ; i<k ; ++i) centroids[i]=new Point(5);


-5
2017-11-29 23:10



是的没有。这不是Java。 - szczurcio
你知道,这实际上可行! - Jack Thomas
不,这实际上无法奏效 - M.M
是的,我不能这样做#1,因为我没有默认的ctor:| - Jack Thomas
请注意,此解决方案提供了不同的类型 centroids;在原来,它是一个 Point* 在你的解决方案中,它变成了 Point**。这应该在答案中更加明显。 - MicroVirus


我建议用一个 std::vector

std::vector<Point> v(k, Point{5});

但你也可以这样做:

Point* centroids = new Point[5]{{1}, {2}, {3}, {4}, {5}};

现场演示


11
2017-11-29 23:18



我不知道那样! - Neil Kirk
这种非向量解决方案是否也适用于 Point 不是聚合? - MicroVirus
@MicroVirus是的,如果你提供一个带有的构造函数 int。 - szczurcio
@szczurcio这个 带构造函数的现场演示 不编译。 - MicroVirus
@MicroVirus有趣的是,同样的代码在VS15(MSVC 14.0)中编译得很好。克朗说 candidate constructor not viable 因为它 requires single argument 'x', but no arguments were provided这很奇怪,但我对标准的解释可能会有误。其中一个编译器以非标准方式运行。 - szczurcio


如果你不能使用 std::vector,然后一个选项是动态分配一个指针数组,然后动态分配n个对象,并将结果内存分配给数组中的指针。例如:

constexpr auto ARRAYSIZE = 5;

auto x = new PointPtr[ARRAYSIZE];  // should check for memory alloc errors
for (int i = 0; i < ARRAYSIZE; ++i)
{
    x[i] = new Point(5); // pass any arguments you want, remember to check if allocation was successful
}

请注意,这种做法不赞成因为你真的不应该使用 new 除非你有充分的理由这样做(而IMO是愚蠢的,你不允许以正确的方式做事并从一开始就教好的做法);改为使用 std::vector 和智能指针,他们应该能够满足你所有的动态内存需求。


3
2017-11-29 23:23



是的,谢谢。请阅读编辑 - Jack Thomas


注1:使用标准库(即 std::vector 在这种情况下)处理事情是优惠的!

注意2:就个人而言,我不会去指针路由数组,因为你破坏了你的记忆位置。

您可以使用 std::allocator :

// Create allocator object
std::allocator<Point> alloc;
// allocate storage for k Points
Point * p = alloc.allocate(k);
// Construct k Points in p
for (std::size_t i{0}; i<k; ++i)
{
  alloc.construct(p+i, 5);
}
// Do stuff using p
// ...
// Destroy k objects in p
for (std::size_t i{0}; i<k; ++i)
{
  alloc.destroy(p+i);
}
// Dealloacte memory
alloc.deallocate(p, k);

或者你可以手动处理它

// allocate
Point * p = static_cast<Point*>(::operator new[](k*sizeof(Point)));
// placement new construction
for (std::size_t i{0}; i<k; ++i)
{
  new((void *)(p+i)) Point{5};
}
// stuff
// destruction
for (std::size_t i{0}; i<k; ++i)
{
  (p+i)->~Point();
}
// deallocation
::operator delete[](static_cast<void*>(p));

我至少将内存处理包装到函数(如果不是类)中:

#include <new>
#include <utility>
#include <cstddef>

template<class T, class ... Args>
T * new_n(std::size_t const n, Args&&  ... args)
{
  T * p{ (T*)::operator new[](n*sizeof(T)) };
  for (std::size_t i{ 0 }; i < n; ++i) 
  {
    new((void*)(p + i)) T(std::forward<Args>(args)...);
  }
  return p;
}

template<class T>
void remove_n(T * const p, std::size_t const n)
{
  for (std::size_t i{ 0 }; i < n; ++i) (p + i)->~T();
  ::operator delete[]((void*)p);
}

并使用它们

auto p = new_n<Point>(k, 5);
// stuff using k Points in p constructed by passing 5 to constructors
remove_n(p, k);

3
2017-11-29 23:26



所以,正如我在另一条评论中所说,我不能将STL用于此任务。我可以将它用于另一个,但我必须提交这个任务的指针。不管怎么说,还是要谢谢你! - Jack Thomas
std::size_t i{0}  - 这是迄今为止初始化我看到的循环变量的最原始方式:P - szczurcio
std::size_t i{} 也就够了,不是吗?我知道C不允许空括号,但我的印象是C ++没有。 - dreamlax
@JackThomas你可以做什么 std::allocator 手工操作,但手动内存管理通常不是一个好主意。 - Pixelchemist
这是不正确的:在remove_n中,数组元素需要以相反的顺序进行破坏! - Andreas H.