C++ 离开作用域后如何保护局部变量的值?

C++ 离开作用域后如何保护局部变量的值?,c++,C++,我将C库的订阅> /COD>函数封装到高级C++类的方法中。在该方法中,使用作为引用传入的本地char*变量(char key[10])调用底层Csubscribe。现在的问题是——我刚才已经解决了——因为key是一个局部变量,它的值不受保护。我可以传递它的引用,但一旦作用域离开,内存将被释放。我遇到了这样的未定义行为,回调从未被调用-调试后,我看到键的值被更改 我试过使用newchar[10],这似乎很有效。但我想这不是我应该去的地方 正确的解决方案是什么更新:现在可以用字符串替换它 更新 接

我将C库的<代码>订阅> /COD>函数封装到高级C++类的方法中。在该方法中,使用作为引用传入的本地
char*
变量(
char key[10]
)调用底层C
subscribe
。现在的问题是——我刚才已经解决了——因为
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
?是否将
任务
返回给调用方以供参考?另外,helper
get\u pvoid()
的返回值指向用户数据,即lambda函数,可能应该保护它?

实现这一点的方法是在堆上分配内存(
new
malloc
)。您有几个选择:

  • 首选选项:如果您
    新建
    内存,您可以将指针包装到
    std::unique\u ptr
    std::shared\u ptr中,并安全地传递它。当最后一个实例超出范围时,内存将自动释放。使用
    get()
    可以直接访问指针。你必须确保没有其他人释放你的内存
  • 使用RAII(资源获取是初始化)-包装器类型,它可以执行
    malloc
    free
    ,或者
    new
    delete
    。在本例中,您通过构造函数分配内存,并通过析构函数释放内存,同时将原始指针传递给C接口。缺点是您必须实现复制/移动语义以确保不会发布两次,并且正确跟踪存储此指针的所有副本。由于涉及的复杂性,我建议您尽可能使用
    唯一/共享\u ptr
    。还可以将自定义删除程序传递给
    共享\u ptr
    ,因此您也可以将其与
    免费
    一起用作删除程序
  • 您可以使用new中的原始指针,但必须确保只删除一次

  • 在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