C++ 离开作用域后如何保护局部变量的值?
我将C库的<代码>订阅> /COD>函数封装到高级C++类的方法中。在该方法中,使用作为引用传入的本地C++ 离开作用域后如何保护局部变量的值?,c++,C++,我将C库的订阅> /COD>函数封装到高级C++类的方法中。在该方法中,使用作为引用传入的本地char*变量(char key[10])调用底层Csubscribe。现在的问题是——我刚才已经解决了——因为key是一个局部变量,它的值不受保护。我可以传递它的引用,但一旦作用域离开,内存将被释放。我遇到了这样的未定义行为,回调从未被调用-调试后,我看到键的值被更改 我试过使用newchar[10],这似乎很有效。但我想这不是我应该去的地方 正确的解决方案是什么更新:现在可以用字符串替换它 更新 接
char*
变量(char key[10]
)调用底层Csubscribe
。现在的问题是——我刚才已经解决了——因为key
是一个局部变量,它的值不受保护。我可以传递它的引用,但一旦作用域离开,内存将被释放。我遇到了这样的未定义
行为,回调从未被调用-调试后,我看到键
的值被更改
我试过使用newchar[10]
,这似乎很有效。但我想这不是我应该去的地方
正确的解决方案是什么更新:现在可以用字符串
替换它
更新
接口功能:
IoT_Error_t aws_iot_mqtt_subscribe(AWS_IoT_Client *pClient, const char *pTopicName, uint16_t topicNameLen,
QoS qos, pApplicationHandler_t pApplicationHandler, void *pApplicationHandlerData)
template<class F>
struct c_style_callback_t {
F f;
template<class...Args>
static void(*get_callback())(Args..., void*) {
return [](Args...args, void* fptr)->void {
(*static_cast<F*>(fptr))(std::forward<Args>(args)...);
};
}
void* get_pvoid() {
return std::addressof(f);
}
};
template<class F>
c_style_callback_t< std::decay_t<F> >
c_style_callback( F&& f ) { return {std::forward<F>(f)}; }
包装器:
std::function<void()> AWS::subscribe(const std::string &topic, std::function<void(const std::string&)> callback, QoS qos) {
ESP_LOGI(TAG, "subscribe: %s", topic.c_str());
std::string key("Test...");
auto task = c_style_callback(
[=] (AWS_IoT_Client *pClient, char *topicName, uint16_t topicNameLen, IoT_Publish_Message_Params *params) {
std::string json;
json.assign((char *)params->payload, (size_t)params->payloadLen);
ESP_LOGI(TAG, "subscribe cb payload=%s", json.c_str()); // works
ESP_LOGI(TAG, "key '%s'", key.c_str()); // undefined behaviour
callback(key); // error, exit
callback(json);
}
);
m_error = ::aws_iot_mqtt_subscribe(
&m_client,
key.c_str(),
key.length(),
qos,
task.get_callback<AWS_IoT_Client*, char*, uint16_t, IoT_Publish_Message_Params*>(),
task.get_pvoid()
);
if (m_error != SUCCESS) {
ESP_LOGD(TAG, "subscribe: error=%d", m_error);
return nullptr;
}
return [=] () {
ESP_LOGI(TAG, "unsubscribe %s", key.c_str()); // works
callback(key); // works
};
} // subscribe
调用subscribe
包装器的主要任务:
{
...
aws->subscribe(
topic,
[&] (const std::string &json) -> void {
ESP_LOGI(TAG, "got json: %s", json.c_str());
}
);
...
}
更新#2
c\u style\u回调
中的回调lambda无法访问callback
和键
的正确值。如何保护这2个文件不被覆盖?将它们“包装”到一个唯一的\u ptr
?是否将任务
返回给调用方以供参考?另外,helperget\u pvoid()
的返回值指向用户数据,即lambda函数,可能应该保护它?实现这一点的方法是在堆上分配内存(new
,malloc
)。您有几个选择:
新建
内存,您可以将指针包装到std::unique\u ptr
或std::shared\u ptr
中,并安全地传递它。当最后一个实例超出范围时,内存将自动释放。使用get()
可以直接访问指针。你必须确保没有其他人释放你的内存李>
malloc
和free
,或者new
和delete
。在本例中,您通过构造函数分配内存,并通过析构函数释放内存,同时将原始指针传递给C接口。缺点是您必须实现复制/移动语义以确保不会发布两次,并且正确跟踪存储此指针的所有副本。由于涉及的复杂性,我建议您尽可能使用唯一/共享\u ptr
。还可以将自定义删除程序传递给共享\u ptr
,因此您也可以将其与免费
一起用作删除程序在C++中,通过传入代码> const < /Cord>数据来完成这一操作。如果你想要一个C++答案,请不要标记为C和C++。标签C已经被删除了。如果这个值会被损坏,请制作一个拷贝。请把接口函数的声明和一个你试图调用它的代码的例子贴在一起。在你的代码中有一个注释:每当你使用数组new时,你肯定是做了一些错误的事情。改用
std::vector
。即使使用普通的新代码通常也是坏代码的标志。这很有意义,我会尝试首选的方法。谢谢应避免裸新建
/删除
。可能会提到std::make_unique
/std::make_shared
。@midor您认为哪一个最适合解决此问题<代码>标准::唯一\u ptr或标准::共享\u ptr
?你能添加一些简单的代码来使用其中的一个吗?@haxpanel一般经验法则:从限制性最强的std::unique_ptr开始,并削弱限制性,在这种情况下,只有在被迫这样做的情况下,才使用std::shared_ptr。提示在名称中:shared\u ptr
如果对象是共享的,则应使用。如果多个其他对象在该对象中拥有所有权权益——只要有任何持有者在使用该对象,就不能销毁该对象——只有在这种情况下,您才应该使用共享\u ptr
。如果必须选择作为函数返回值,我会选择唯一的\u ptr
,因为它可以隐式转换为共享的\u ptr
。