在堆上分配内存的函数原型(C / C ++)

时间:2009-07-23 13:55:17

标签: c++ c heap buffer

我对C ++很新,所以这可能是一个初学者的问题。它认为做某些我认为相当普遍的事情的“适当”风格。​​

我正在编写一个函数,在执行其职责时,在堆上分配内存以供调用者使用。我很好奇这个函数的原型应该是什么样子。现在我有:

int f(char** buffer);

要使用它,我会写:

char* data;
int data_length = f(&data);
// ...
delete[] data;

但是,我正在向指针传递一个指针,这让我觉得我可能做错了。

有人想开导我吗?

12 个答案:

答案 0 :(得分:6)

在C中,这或多或少是合法的。

在C ++中,函数通常不应该这样做。您应该尝试使用RAII来保证内存不会泄露。

现在你可能会说“它会如何泄漏内存,我只是在那里调用delete[]!”,但如果在// ...行引发异常怎么办?

根据功能的确切功能,您可以考虑以下几个选项。一个显而易见的是用向量替换数组:

std::vector<char> f();

std::vector<char> data = f();
int data_length = data.size();
// ...
//delete[] data; 

现在我们不再需要显式删除,因为向量是在堆栈上分配的,当析构函数超出范围时会调用它的析构函数。

我应该在回应评论时提到,上述内容暗示了向量的副本,这可能很昂贵。如果f函数不是太复杂,大多数编译器都会优化该副本,这样就可以了。 (如果没有经常调用该函数,那么开销无关紧要无论如何)。但是如果没有发生这种情况,你可以通过引用将空数组传递给f函数,并让f将数据存储在其中,而不是返回一个新的向量。

如果返回副本的性能是不可接受的,另一种选择是完全解耦容器的选择,而是使用迭代器:

// definition of f
template <typename iter>
void f(iter out);

// use of f
std::vector<char> vec;
f(std::back_inserter(vec));

现在可以使用通常的迭代器操作(*out来引用或写入当前元素,并++out将迭代器前移到下一个元素) - 更重要的是,所有的标准算法现在可以工作。例如,您可以使用std::copy将数据复制到迭代器。这是标准库通常选择的方法(即,这是一个好主意;))当函数必须返回一系列数据时。

另一种选择是让你自己的对象负责分配/解除分配:

struct f { // simplified for the sake of example. In the real world, it should be given a proper copy constructor + assignment operator, or they should be made inaccessible to avoid copying the object
  f(){
    // do whatever the f function was originally meant to do here
    size = ???
    data = new char[size];
  }
  ~f() { delete[] data; }

int size;
char* data;
};

f data;
int data_length = data.size;
// ...
//delete[] data; 

我们再次不再需要显式删除,因为分配是由堆栈上的对象管理的。后者显然是更多的工作,并且存在更多的错误空间,因此如果标准的矢量类(或其他标准库组件)完成工作,则更喜欢它们。此示例仅在您需要根据您的情况定制的内容时才会显示。

C ++中的一般经验法则是“如果你在RAII对象之外写deletedelete[],那你做错了。如果你正在编写{{在RAII对象之外的1}}或`new [],你做错了,除非结果立即传递给智能指针“

答案 1 :(得分:5)

在“正确的”C ++中,您将返回一个包含内部某处内存分配的对象。类似于std :: vector。

答案 2 :(得分:4)

您的函数不应该返回指向某个内存的裸指针。毕竟,指针可以被复制。然后你有所有权问题:谁实际拥有内存并应删除它?您还有一个问题,即裸指针可能指向堆栈上,堆上的单个对象或静态对象。它也可以指向这些地方的阵列。鉴于您返回的只是一个指针,用户应该如何知道?

您应该做的是返回以适当方式管理其资源的对象。 (查看RAII。)假设这种情况下的资源是char的数组,std::stringstd::vector似乎是最好的:

int f(std::vector<char>& buffer);

std::vector<char> buffer;
int result = f(buffer);

答案 3 :(得分:3)

为什么不采用与malloc()相同的方式 - void * malloc(size_t numberOfBytes)?这样,字节数是输入参数,分配的块地址是返回值。

UPD: 在评论中你说f()除了分配内存之外基本上执行一些动作。在这种情况下,使用std :: vector是一种更好的方法。

void f( std::vector<char>& buffer )
{
    buffer.clear();
    // generate data and add it to the vector
}

调用者只会传递一个已分配的向量:

 std::vector buffer;
 f( buffer );
 //f.size() now will return the number of elements to work with

答案 4 :(得分:2)

通过引用传递指针......

int f(char* &buffer)

但是,您可能希望考虑使用引用计数指针(例如boost :: shared_array)来管理内存,如果您刚开始这样做的话。

e.g。

int f(boost::shared_array<char> &buffer)

答案 5 :(得分:1)

答案 6 :(得分:0)

只需返回指针:

char * f() {
   return new char[100];
}

话虽如此,您可能不需要像这样混淆显式分配 - 而不是char数组,而是使用std::stringstd::vector<char>

答案 7 :(得分:0)

如果f要匹配new[],它会起作用,但它不是非常惯用的。

假设f填写数据并且不仅仅是malloc() - 你可以更好地将分配包装为std::vector<char>

void f(std::vector<char> &buffer)
{
    // compute length
    int len = ...

    std::vector<char> data(len);

    // fill in data
    ...

    buffer.swap(data);
}

编辑 - 从签名中删除虚假*

答案 8 :(得分:0)

实际上,聪明的做法是将指针放在一个类中。通过这种方式,您可以更好地控制其破坏,并且界面对用户来说更不容易混淆。

class Cookie {
public:
   Cookie () : pointer (new char[100]) {};
   ~Cookie () {
      delete[] pointer;
   }
private:
   char * pointer;

   // Prevent copying. Otherwise we have to make these "smart" to prevent 
   // destruction issues.
   Cookie(const Cookie&);
   Cookie& operator=(const Cookie&);
};

答案 9 :(得分:0)

如果缓冲区的所有f()都返回它(及其长度),那么让它返回长度,然后让调用者new。如果f()也对缓冲区做了某些事情,那么请按照多语言建议。

当然,对于您想要解决的问题可能有更好的设计,但是我们建议任何事情都需要您提供更多的背景。

答案 10 :(得分:0)

正确的样式可能不是使用char *而是使用std :: vector或std :: string,具体取决于你使用char *的内容。

关于传递要修改的参数而不是传递指针的问题,传递引用。在你的情况下:

int f(char*&);

如果您遵循第一条建议:

int f(std::string&);

int f(std::vector<char>&);

答案 11 :(得分:-2)

我猜你正在尝试分配一维数组。如果是这样,则不需要将指针传递给指针。

int f(char* &buffer)

应该足够了。使用场景将是:

char* data;
int data_length = f(data);
// ...
delete[] data;