在C++中返回指针

Returning a pointer in C++

本文关键字:指针 返回 C++      更新时间:2023-10-16

我认为我的问题听起来很愚蠢,欢迎对我投反对票。如果您在C++中实现需要返回指针的方法,这样做是否安全?如果没有,为什么?

不是一个简单的问题。例如:返回指针的最佳方式。

理想情况下,应尽量避免返回带有副作用或义务的值。

// This may be ok, it implies no burden on the user.
Manager* GetManager();
// But what if the user decides to call delete on the value you return?
// This is not unusual in C code, but carries a hidden contract:
// I allocate - you free.
const char* GetFilename(int fd)
{
    char* filename = malloc(256);
    sprintf(filename, "/tmp/tmpfile.%d", fd);
    return filename;
}

C++是关于封装和抽象的。您可以通过封装要返回的指针来编纂与使用者的协定。这里的想法是,不是公开指针,而是公开一个负责指针所有权的对象。事实上,该语言的最新版本已经通过 std::unique_ptr、std::shared_ptr 和 std::weak_ptr 为您执行此操作。

但一个粗略、简单的 RAII 示例可能是:

class StrDupPtr
{
    char* m_alloc;
public:
    StrDupPtr(const char* src)
        : m_alloc(strdup(src))
    {}
    ~StrDupPtr()
    {
        free(m_alloc);
    }
    operator const char* () const { return m_alloc; }
    // etc.
};

你仍然在此处返回一个指针,但你已使用管理协定封装了它,并消除了最终用户管理资源的负担。

你不能总是避免它,当你不得不这样做时,是的,它可能很危险。

int* AllocateMeSomeMemory()
{
    int* memory = malloc(4 * sizeof(int));
    // here, have four ints.
    return memory;
}
int main() {
    int* memory = AllocateMeSomeMemory();
    memory[42] = 0xDeath; // yeah, it's not a valid hex number, but that's not really the problem.
}

指针的另一个常见问题是,无法判断有多少人拥有指针。下面是一个人为的例子:

void transferItem(userid_t user1, userid_t user2, itemid_t item) {
    Account* a1 = GetAccount(user1);
    Account* a2 = GetAccount(user2);
    if (a1 != a2) {
        transferItemInternal(a1, a2, item);
    }
    delete a2;
    delete a1;  // Sorry Dave, I can't do that. How about a nice game of CRASH?
}

通常,a2 和 a1 会有所不同,但是当它们不是时......

指针的另一个常见故障模式是异步回调:

// ask the database for user details, call OnLoginResult with userObj when we're done.
void login(int socket, userid_t userId, passwordhash_t pass) {
    User* userObj = GetUserObj(userId, socket);
    Query* query = Database()->NewQuery("SELECT * FROM user WHERE id = ? AND password = ?", userId, pass);
    Database()->Queue(query, OnLoginResult, userObj);
}
void OnDisconnect(int socket, int reason) {
    User* userObj = GetUserBySocket(socket);
    if (userObj) {
        UnregisterUserObj(userObj);
        delete userObj;
    }
}
void OnLoginResult(void* param) {
    User* userObj = static_cast<UserObj*>(param);
    // all well and good unless the user disconnected while waiting.
    ...
}

是的。我假设你的意思是"分配和返回"一个指针。

通常具有初始化函数,这些函数将指针分配给某种类型的对象,然后初始化对象本身。然后,程序的不同部分将释放内存。

好吧,

这总是取决于你在做什么。指针只是一个内存地址,因此它类似于简单地返回一个整数。你应该对指针以及如何正确实现它们做更多的研究

我觉得这个问题可能很快就会结束,但无论如何我都会尝试回答。

是的,它是"安全的",只要你小心。事实上,这是一种非常常见的做事方式,尤其是在与 C API 交互时。话虽如此,如果可以的话,最好避免这样做,因为C++通常会提供更好的选择。

为什么要避免它?首先,假设您有一个如下所示的方法:

MyStruct* get_data();

返回值是指向MyStruct单个实例的指针,还是数组的开头?是否需要free()返回的指针?或者也许你需要使用delete?返回值是否可以NULL,如果是,会发生什么?如果不查看文档,您将无法了解这些内容中的任何内容。而且编译器也无法知道,因此它无法以任何方式帮助您。

更好的选择:

如果要返回值数组,请使用std::array(如果大小在编译时是固定的)或std::vector(如果大小在运行时之前未知)。

如果尝试避免复制大型结构,请返回引用或 const 引用(如果可能)。这样,调用方就知道他们不会收到NULL值。

如果你真的需要返回一个指针,那么考虑使用智能指针来代替——这将有助于你解决所有权问题。例如,std::shared_ptr使用引用计数,std::unique_ptr确保给定指针只有一个所有者。