c ++ - 必须定义所有静态类方法,即使没有使用它们吗?

时间:2017-09-13 22:53:05

标签: c++ c++11 gcc

所以,我遇到了一个问题,我不确定它是语言问题还是编译器/ GCC问题。

TL; DR - 我是否需要定义类中的所有静态方法,即使这些静态方法从不调用应用程序(即可以通过链接器合法地删除)?

我有一个库类,它在微控制器中实现UART的设备驱动程序。因为我不希望多个UART对象指向同一个资源,所以每个UART对象都是一个单例,使用多个GetInstance()方法之一检索,一个用于设备中的每个UART实例(UART0,UART1等) )。每个UART实例需要有两个FIFO(Tx和Rx)用于存储。每个FIFO都需要由应用程序明确调整大小,并在实例化UART对象时进行分配(理想情况下)。所以我也有几个静态GetStorage()方法,每个UART一次。

我为我的概念验证创建了一些精简代码。这是static_instance.h:

#ifndef STATIC_INSTANCE_H_
#define STATIC_INSTANCE_H_

#ifdef __cplusplus
#include <vector>
namespace foo {
class Uart {
 public:
  /* Retrieve a singleton instance, using lazy static initialization.  Note
   * that not all instances will be present for a given device. */
  static Uart& Uart1GetInstance(void);
  static Uart& Uart2GetInstance(void);
  static Uart& Uart3GetInstance(void);
  /* Does something. */
  void DoSomething(void) { ++counter; }
 private:
  /* Structure for the storage that each static Uart instance requires. */
  struct Storage {
    Storage(std::vector<char>& vector)
        : my_vector_(vector) { }
    std::vector<char>& my_vector_;   // Buffer for data.
  };
  /* Instantiate object using provided register base and FIFO structures. */
  Uart(int instance, Storage& storage)
      : instance_(instance), storage_(storage) { }
  ~Uart() { }
  /* Retrieves the storage required for the static Uart object instances.
   * These methods are NOT implemented in static_instance.cc, but must be
   * implemented in the application code, only for those Uart instances
   * that are invoked in the application. */
  static Storage& Uart1GetStorage(void);
  static Storage& Uart2GetStorage(void);
  static Storage& Uart3GetStorage(void);
  int const instance_;    // Instance number of this object.
  Storage& storage_;      // Allocated storage for this object.
  int counter = 0;        // Dummy counter.
};
} // namespace foo
#endif  // __cplusplus

#endif

这里是static_instance.cc:

#include <static_instance.h>
namespace foo {

Uart& Uart::Uart1GetInstance(void) {
  static Uart uart(1, Uart1GetStorage());
  return uart;
}
Uart& Uart::Uart2GetInstance(void) {
  static Uart uart(2, Uart2GetStorage());
  return uart;
}
Uart& Uart::Uart3GetInstance(void) {
  static Uart uart(3, Uart3GetStorage());
  return uart;
}

} // namespace foo

我们的想法是,您只需为实际需要的UART实例调用GetInstance(),然后仅为该UART实例定义GetStorage()。 (对于这个例子,我只定义了一个缓冲区,并使用std::vector<char>作为替身。)此外,它还留给应用程序来定义存储方法,因为每个应用程序都是对给定的UART缓冲区需要多大有自己的要求。 (我绝对不会做的是将宏放在我的C ++模块中,因为,呃。)这是main.cc中的代码片段来实例化UART2:

namespace foo {

Uart::Storage& Uart::Uart2GetStorage(void) {
  static std::vector<char> rx_vector(256, 0);
  static Uart::Storage storage(rx_vector);
  return storage;
}
static foo::Uart& uart_ = foo::Uart::Uart2GetInstance();
void wibble(void) {
  uart_.DoSomething();
}

} // namespace foo

现在,我正在开发一个早期的应用程序,使用早期的IDE用于此芯片(Kinetis Design Studio v3.2.0用于好奇),它使用 GCC 4.8.4 并编译并链接没有错误

但恩智浦已将KDS弃用于另一个使用 GCC 5.4.1 的工具链(MCUXpresso 10.0),并使用完全相同的代码,这次我得到两个链接器错误

./source/static_instance.o: In function 'foo::Uart::Uart1GetInstance()':
../source/static_instance.cc:5: undefined reference to 'foo::Uart::Uart1GetStorage()'
./source/static_instance.o: In function 'foo::Uart::Uart3GetInstance()':
../source/static_instance.cc:13: undefined reference to 'foo::Uart::Uart3GetStorage()'

我不确定为什么链接器会关注UART1和UART3的GetStorage()方法没有被定义,因为我没有为UART1调用GetInstance()或者UART3在我的应用程序中,因此从不调用相应的GetStorage()方法。

我的问题是...... C ++ 11 是否需要我在我的可执行文件中定义了所有三种存储方法?那就是GCC 4.8.4让我逃脱了我不应该做的事情吗?或者这是我需要切换的一些GCC 5.4选项,以允许我从类中删除未使用的静态成员吗​​?

如果答案是&#34;你必须定义它们而不管&#34;,然后我将定义它们,或者设计一些其他方式来允许。如果答案是&#34;那应该是好的&#34;,并且我无法在命令行上设置以使GCC 5.4做到这一点,那么我将采取下一步措施,报告恩智浦论坛中的错误。感谢。

2 个答案:

答案 0 :(得分:4)

  

TL; DR - 我是否需要在类中定义所有静态方法,即使这些静态方法从未被应用程序调用

您可能需要定义此类静态方法,即使它们从未被应用程序调用过。

但是,一般情况下,如果这些函数不是odr-used(一个定义规则),则可能不需要。对于静态成员函数,这相当于

  

一个名称显示为潜在评估表达式的函数

差异很微妙。我希望这能证明这一点:

if(false)
    function();

function永远不会被调用,但会出现在潜在评估的表达式中,因此使用起来很有用,因此必须进行定义。

  

我的问题是...... C ++ 11是否要求我在我的可执行文件中定义所有三种存储方法?

是。它们都出现在潜在评估的表达式中,因此使用起来很有用,因此必须进行定义。

  

我正在使用... GCC 4.8.4开发一个早期的应用程序,并且编译和链接没有错误。

错误违规行为有未定义的行为,这就解释了为什么你没有在另一个工具链中收到错误/警告。

答案 1 :(得分:2)

  

[basic.def.odr]

     
      
  1. 每个程序应该只包含每个非内联函数或变量的一个定义   该程序在废弃的陈述之外(9.4.1);无需诊断。
  2.   

这是一个相对较新的草案标准,但所有版本都包含类似的声明。

“无需诊断”子句为您的编译器提供了接受程序的权限,即使它违反了规则。如果合理的话,在无法访问的代码段中使用odr就是这样:编译器可能会也可能不会优化包含违规调用的死代码。您的程序仍然违规,另一个实施可能会拒绝它。