使用构造函数来分配内存

时间:2016-03-30 19:55:40

标签: c++ constructor

我有一个包含几个数组的类,其大小可以通过其构造函数的参数来确定。我的问题是这个类的实例的大小无法在编译时确定,我不知道如何在运行时告诉 new 方法我需要多大的对象是。每个对象都是固定大小的,但不同的实例可能有不同的大小。

有几种解决问题的方法:
- 使用工厂
- 使用放置构造函数
- 在构造函数中分配数组并在我的对象中存储指向它们的指针。

我正在使用 C 编写的旧应用程序调整一些遗留代码。在原始代码中,程序计算出整个对象需要多少内存,为该数量调用malloc(),然后继续初始化各个字段。

对于C ++版本,我希望能够为我的对象创建一个(相当)正常的构造函数。它将是父类的后代,并且一些代码将依赖于多态来调用正确的方法。来自同一父级的其他类在编译时已知大小,因此没有问题。

我想避免在使用展示位置新时需要考虑的一些特殊注意事项,并且我希望能够以正常方式删除对象。

我想避免在我的对象体内携带指针,部分是为了避免与复制对象相关的所有权问题,部分是因为我想尽可能多地重用现有的C代码。如果所有权是唯一的问题,我可能只是使用共享指针而不用担心。

以下是创建对象的C代码的精简版:

typedef struct
{
  int controls;
  int coords;
} myobject;
myobject* create_obj(int controls, int coords)
{
  size_t size = sizeof(myobject) + (controls + coords*2) * sizeof(double);
  char* mem = malloc(size);
  myobject* p = (myobject *) mem;
  p->controls = controls;
  p->coords = coords;
  return p;
}

对象内的数组维持对象生命周期的固定大小。在上面的代码中,遵循myobject结构的内存将用于保存数组元素。

我觉得我可能会遗漏一些明显的东西。有什么方法我不知道在C ++中编写一个(相当)正常的构造函数,但是能够告诉它在运行时对象需要多少内存,而不需要求助于“放置新的”场景?

5 个答案:

答案 0 :(得分:2)

实用方法如何:保持结构不变(如果与C的兼容性很重要)并将其包装到c ++类中?

typedef struct
{
  int controls;
  int coords;
} myobject;

myobject* create_obj(int controls, int coords);
void dispose_obj(myobject* obj);


class MyObject
{
public: 
   MyObject(int controls, int coords) {_data = create_obj(controls, coords);}
  ~MyObject() {dispose_obj(_data);}

   const myobject* data() const
   {
       return _data;
   }

   myobject* data() 
   {
       return _data;
   }

   int controls() const {return _data->controls;}
   int coords() const {return _data->coords;} 
   double* array() { return (double*)(_data+1); }

 private:
     myobject* _data;
}

答案 1 :(得分:1)

为了维护原始代码的语义,其中struct和array位于一个独特的内存块中,您只需将malloc(size)替换为new char[size]代替:

myobject* create_obj(int controls, int coords)
{
  size_t size = sizeof(myobject) + (controls + coords*2) * sizeof(double);
  char* mem = new char[size];
  myobject* p = new(mem) myobject;
  p->controls = controls;
  p->coords = coords;
  return p;
}

在使用delete[]释放内存时,您必须使用类型转换,但是:

myobject *p = create_obj(...);
...
p->~myobject();
delete[] (char*) p;

在这种情况下,我建议将该逻辑包装在另一个函数中:

void free_obj(myobject *p)
{
    p->~myobject();
    delete[] (char*) p;
}

myobject *p = create_obj(...);
...
free_obj(p);

话虽如此,如果你被允许,最好重新编写代码来代替C ++语义,例如:

struct myobject
{
    int controls;
    int coords;
    std::vector<double> values;

    myobject(int acontrols, int acoords) :
        controls(acontrols),
        coords(acoords),
        values(acontrols + acoords*2)
    {
    }
};

然后你可以这样做:

std::unique_ptr<myobject> p = std::make_unique<myobject>(...); // C++14
...

std::unique_ptr<myobject> p(new myobject(...)); // C++11
...

std::auto_ptr<myobject> p(new myobject(...)); // pre C++11
...

答案 2 :(得分:1)

虽然我理解限制对现有C代码进行更改的愿望,但最好现在正确地执行它,而不是在将来与bug作斗争。我建议你对代码进行以下结构和更改来处理它(我怀疑它主要是提取计算偏移量的代码)。

    struct spots
    {
        double x;
        double y;
    };

    struct myobject 
    {
        std::vector<double> m_controls;
        std::vector<spots>  m_coordinates;

        myobject( int controls, int coordinates ) :
           m_controls( controls ), 
           m_coordinates( coordinates ) 
        { }
    };

答案 3 :(得分:-1)

当评论最终对实际要求有所了解时,解决方案将如下:

  • 分配一个足够大的缓冲区来容纳你的对象和数组
  • 在缓冲区的开头使用placement new

以下是:

class myobject {
   myobject(int controls, int coords) : controls(controls), coords(coords) {}
   ~myobject() {};
public:
   const int controls;
   const int coords;
   static myobject* create(int controls, int coords) {
       std::unique_ptr<char> buffer = new char[sizeof(myobject) + (controls + coords*2) * sizeof(double)];
       myobject obj* = new (buffer.get()) myobject(controls, coords);
       buffer.release();

       return obj;
   }
   void dispose() {
       ~myobject();
       char* p = (char*)this;
       delete[] p;
   }
};

myobject *p = myobject::create(...);
...
p->dispose();

(或适当包装在智能指针的删除器中)

答案 4 :(得分:-1)

新答案(由OP发表评论):

分配正确大小的std::vector<byte>。分配给向量的数组将是连续的内存。可以计算此向量大小,向量将正确管理您的内存。您仍然需要非常小心如何管理对该字节数组的访问,但至少可以使用迭代器等(如果需要)。

顺便说一下,这是一个小模板的东西,我用它来移动字节blob更加优雅(注意这有别名问题,正如谢尔盖在下面的评论中所指出的那样,我&#39 ;我把它留在这里,因为它似乎是一个不做的好例子...... :-)):

template<typename T>
T readFromBuf(byte*& ptr) {
    T * const p = reinterpret_cast<T*>(ptr);
    ptr += sizeof(T);
    return *p;
}

旧答案:

正如评论所示,您可以轻松使用std::vector来做您想做的事情。我还想提出另一个建议。

size_t size = sizeof(myobject) + (controls + coords*2) * sizeof(double);

上面一行代码告诉我你有一些隐藏的结构&#34;在你的代码中。您的myobject结构有两个int值,您可以从中计算实际需要的大小。你真正需要的是:

struct ControlCoord {
  double control;
  std::pair<double, double> coordinate;
};
std::vector<ControlCoord>> controlCoords;