C ++ - 如何使用new运算符安全地包装malloc或emulate

时间:2014-09-05 20:25:42

标签: c++ c new-operator type-safety alloc

在C ++中用函数包装malloc是否有一种普遍接受的安全方法?我试图做的是分配任意大小的内存块,用于保存将二进制值写入固定大小缓冲区的函数的输出(即:机器指令)。我目前使用的方法是这样的:

class voidp {
  void *p;
public:
  voidp (void *pp) : p (pp) {}
  template<class T> operator T *() { return (T *) p; }
};

When converting C to C++, you can define malloc like this:

inline voidp
mymalloc (size_t size)
{
  return malloc (size);
}
#define malloc mymalloc

在许多情况下,似乎不允许对(void*)进行显式强制转换(至少在我的编译器中),我必须使用上面基于模板的方法。

这种做法是否安全,是否对三&#34;规则有任何影响? (即:我需要禁用或明确定义/重载的构造函数吗?)

谢谢。

参考

  1. 我对C ++的操作员新手 - 在类型系统中打破整体,访问2014-09-05,<http://www.scs.stanford.edu/~dm/home/papers/c++-new.html>
  2. 在C面试问题中解决内存对齐困扰我,访问2014-09-05,<https://stackoverflow.com/questions/227897/solve-the-memory-alignment-in-c-interview-question-that-stumped-me>
  3. 修改


    我这样做的原因是因为我尝试使用的东西可能比通过void* buffer = new char[100]分配一个字符块更好。为了进一步说明,我正在编写一些低级代码,根据我的建议,必须用C ++编写,而不是纯C.我还需要动态内存分配方法来创建内存块堆上的16字节,32字节和64字节对齐,如下面的示例中的16字节对齐。

    {
      void *mem = malloc(1024+15);
      void *ptr = ((uintptr_t)mem+15) & ~ (uintptr_t)0x0F;
      memset_16aligned(ptr, 0, 1024);
      free(mem);
    }
    

    我的应用程序实际上在块中创建一组低级机器指令,这些块必须是16/32/64字节对齐(或者它将触发CPU级错误),然后将它们传递给板载DSP芯片,它将它们作为实际指令运行,而不仅仅是输入数据。

3 个答案:

答案 0 :(得分:2)

主题1:旧的void*

C ++的输入类型比C强。 这不是问题,而是一个解决方案! 编译器以这种方式拦截了许多令人讨厌的问题找出调试时间。

简单的例子。下面的代码在C中编译。当我执行时,我得到一个核心转储。

FILE *fp;                     // file 
void *pbuff;                  // pointer to buffeer
pbuff = malloc(BUFSIZ); 
fp=fopen("test.txt", "rw");
fread(fp, BUFSIZ, 1, pbuff);  // ooops !!  Did you notice ? 

为什么?我倒fp和pbuff。两者都是void*anything*anything*void*结构的受害者。当然,经过实验的程序员不会在标准库上犯这样的错误!但其余的是什么?

使用C ++时,这个错误的代码无法编译,因为将void* pbuff转换为FILE*会被识别为问题。

主题2:模板voidp是否安全?

好吧,C ++有类和继承。还有一大堆演员来强调你可以(并且仍然允许)做的奇怪的指针操作。 C ++再次简化了bug的发现。例如,reinterpret_cast<>比受控制的static_cast<>

更值得关注

要做安全的事情,需要了解C ++对象模型并使用适当的工具。你的voidp()非常聪明,但它不是OOP的合适工具。

简单的例子,首先是本地方式:

   struct A { std::string name; A(std::string s) : name(s) {} };
   struct B : A { int x, y;  B(std::string s, int a, int b) : A{ s }, x(a), y(b) {}  };
    ...
    A a{ "Chris" };
    B b{ "Tophe", 30, 40 };
    A *gpa = &a; 
    B *gpb = &b; 
    gpa = gpb;    // yes, works, because a B is an A and compiler knows it. 
    //gpb = gpa;  // No ! you can't without a recast !  

现在使用voidp方法:

voidp pa(&a); 
voidp pb(&b);
pb = pa;  // no problem.  
gpb = pa;  // no problem ! Not even a cast to draw attention on potential issue !   

所以你看, 它相当不安全! 它确实隐藏了令人讨厌的错误!

主题3:malloc()是否优于new

谦虚地,malloc()您可以轻松创建任何内容。而且创建错误大小的东西也很容易....使用new,你也可以做一些奇怪的事情,但是做基本错误会更困难。

new可以调用对象构造函数。使用malloc(),您需要执行额外的步骤。

然后,当您malloc()时,您拥有free()malloc() / free()非常适合C语言中的被动数据结构。但是在C ++中,当你想要正确删除一个对象时,你必须去掉它。所以delete更合适。

<强>结论

我感兴趣地阅读了您的参考文献。确实,new有局限性。

但我不同意文章的结论。而不是避免new并切换回malloc()(再次:它非常适合C代码,但不是最适合C ++),这将是研究标准库的更好方法,例如使用shared_ptr<>和其他智能指针。这些对于避免内存泄漏要好得多,而不是重写一个自己的malloc版本......

<强> 修改

您的具体用途也可以在C ++中完成

char buffer[1024 + 15];     // allocation  on stack, or char *mem = new char[1024+15]  
char *ptr = reinterpret_cast<char*>(((uintptr_t)&buffer + 15) & ~(uintptr_t)0x0F);   // Yes
std::fill_n(ptr, 0, 1024);     // is memset_16aligned() really portable ?  
// nothing if on stack         // or delete[] mem if new was used                 

还存在函数std::align(),它为ptr执行计算。 并且有一个placement-new可以在固定地址上分配C ++对象,例如:

char* p = new(ptr)char[1024];  // of course you shouldn't delete this one 

答案 1 :(得分:1)

如果您正在编写C ++代码。你应该使用new和delete。在C ++中编写C样式通常是不好的做法,因为C ++编译器不会产生最佳的二进制文件。如果你发现自己做了很多带有void *的类型转换,那么你可能做错了。

相反,使用C ++ 11智能指针!

答案 2 :(得分:0)

newdelete用于低级库开发。 有STL容器可用于缓冲区分配和解除分配。

在您的情况下,您可以尝试

std::vector<unsigned char> myBuffer(MAX_SIZE);