6

我对 C++ 相当陌生,所以这可能是一个初学者的问题。它认为做某事的“正确”风格是相当普遍的。

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

int f(char** buffer);

要使用它,我会写:

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

但是,我将指针传递给指针的事实提示我我可能以错误的方式执行此操作。

有人愿意开导我吗?

4

12 回答 12

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 对象之外编写一个deleteor delete[],那么你做错了。如果你在newRAII 对象之外编写一个 or `new[],你就是在这样做错误,除非结果立即传递给智能指针”

于 2009-07-23T14:09:39.057 回答
5

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

于 2009-07-23T14:00:45.637 回答
4

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

相反,您应该做的是返回一个以适当方式管理其资源的对象。(查找 RAII。)在这种情况下,资源是一个数组char,astd::string或 astd::vector似乎是最好的:

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

std::vector<char> buffer;
int result = f(buffer);
于 2009-07-23T14:06:17.890 回答
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
于 2009-07-23T13:58:18.447 回答
2

通过引用传递指针...

int f(char* &buffer)

但是,如果您刚刚开始,您可能希望考虑使用诸如 boost::shared_array 之类的引用计数指针来管理内存。

例如

int f(boost::shared_array<char> &buffer)
于 2009-07-23T14:00:22.813 回答
1

使用 RAII(资源获取即初始化)设计模式。

http://en.wikipedia.org/wiki/RAII 理解术语和概念的含义 - RAII (Resource Acquisition is Initialization)

于 2009-07-23T14:05:37.533 回答
0

只需返回指针:

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

话虽如此,您可能不需要像这样搞乱显式分配 - 而不是 char 数组,使用std::stringorstd::vector<char>代替。

于 2009-07-23T13:58:21.957 回答
0

如果 fnew[]匹配,它会起作用,但它不是很地道。

假设 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);
}

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

于 2009-07-23T14:02:40.220 回答
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&);
};
于 2009-07-23T14:04:27.893 回答
0

如果f()对缓冲区所做的一切就是返回它(及其长度),让它只返回长度,并让调用者new得到它。如果f()还对缓冲区做了一些事情,那么按照 polyglot 的建议做。

当然,对于您要解决的问题,可能会有更好的设计,但我们提出任何建议都需要您提供更多上下文。

于 2009-07-23T14:05:48.017 回答
0

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

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

int f(char*&);

如果您遵循第一个建议:

int f(std::string&);

或者

int f(std::vector<char>&);
于 2009-07-23T14:07:04.113 回答
-2

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

int f(char* &buffer)

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

char* data;
int data_length = f(data);
// ...
delete[] data;
于 2009-07-23T14:01:37.430 回答