【问题标题】:Is there a standard procedure for passing class objects by value?是否有按值传递类对象的标准程序?
【发布时间】:2014-05-05 17:51:03
【问题描述】:

是否有按值传递类的标准程序?换句话说,如果我这样做:

struct Test
{
  int a;
  double b;
}

void DoSomething(Test t)
{
  std::cout << t.a << std::endl;
  std::cout << t.b << std::endl;
}

//...

Test myObj;
myObj.a = 5;
myObj.b = 10.5;

DoSomething(myObj);

假设标准打包和布局,标准是否提供任何保证,无论编译器如何,都会以一致的方式发送和接收类?


因为我预计会出现“你为什么要这样做?”这样的问题。或“这感觉像是一个 XY 问题”,here's (lengthy) context。我试图在 EXE 和使用不同编译器编译的 DLL 之间来回传递类对象,并且似乎没有从正确的起始地址读取类对象。但是,如果我通过引用传递对象,这个问题就会消失。 (额外的问题 - 为什么在按值传递时按引用传递不起作用?我的印象是按值传递会复制对象并传递对副本的引用。显然我在这里误解了一些东西。)

【问题讨论】:

  • 不,据我所知,该标准没有断言如何传递参数。您要问的是 ABI(应用程序二进制接口)的问题,并且在编译器供应商和版本甚至硬件架构之间有所不同。
  • 正如 Nathan 所说,标准没有说明编译器之间的交互。您需要知道他们的 ABI 是否兼容。
  • 引用传递怎么会起作用呢?这只是运气,还是我错过了价值传递的工作原理?因为如果按引用传递有效,并且按值传递隐含地使用按引用传递但失败了,这会导致一个非常困惑的计算机怪胎。
  • 如果您坚持使用 C,ABI 对于给定平台是固定的。这就是为什么许多库都有一个 C 接口的原因之一,它包装了一个 C++ 接口并提供与类函数相同的参数加上一个指向结构的指针的函数。
  • 引用传递只会传递一个指针,而值传递会将整个对象放入堆栈并调用复制构造函数,这意味着堆栈上的内存布局必须兼容。

标签: c++ winapi c++11 standards


【解决方案1】:

一般来说,不同 C++ 编译器之间的 ABI 可以以任何他们认为合适的方式变化。 C++ 标准不强制要求给定的 ABI。

但是,C ABI 非常稳定。解决此问题的一种方法是使用仅标头函数将代码转换为从 DLL 导出的 extern "C" 函数。在您的 DLL 中,extern "C" 函数然后调用更传统的 C++ 接口。

举个具体的例子,

struct Test;
// DLL exported:
extern "C" void Private_DoSomething_Exported( Test* );

// Interface:
namespace Interface {
  inline void DoSomething( Test t ) { return Private_DoSomething_Exported(&t); }
};

// implementation.  Using Test&& to make it clear that the reference is not an export parameter:
namespace Implementation {
  void DoSomething( Test&&t ) {
    std::cout << t.a << std::endl;
    std::cout << t.b << std::endl;
  }
}
void Private_DoSomething_Exported( Test* t ) {
  Assert(t);
  Implementation::DoSomething(std::move(*t));
}

这会将“最兼容的”ABI(纯“C”ABI)放在您从 DLL 导出函数的位置。客户端代码调用Interface::DoSomething,客户端代码中的inline调用"C" ABI(它甚至不知道对象的布局),然后调用C++ Implementation::DoSomething

这仍然不能证明所有问题,因为即使是 POD 结构的布局也可能因编译器而异(作为一个实际示例,一些编译器在 64 位机器上将 long 视为 32 位,而其他编译器将 long 视为64 位机器上的 64 位)。包装也可能不同。

为了减少这种影响,您首先希望只使用 C 头文件中的固定大小类型。您还需要检查两个编译器的打包文档。

【讨论】:

    猜你喜欢
    • 2012-08-31
    • 2015-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-24
    • 1970-01-01
    • 2010-11-26
    相关资源
    最近更新 更多