c++ - 是不是必须定义所有静态类方法,即使不使用?
Posted
技术标签:
【中文标题】c++ - 是不是必须定义所有静态类方法,即使不使用?【英文标题】:c++ - Must all static class methods be defined, even when not used?c++ - 是否必须定义所有静态类方法,即使不使用? 【发布时间】:2017-09-13 22:53:04 【问题描述】:所以,我遇到了一个问题,我不确定是语言问题,还是编译器/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++ 模块中,因为,ew。)这是 main.cc 中用于实例化 UART2 的代码 sn-p:
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 并与 进行编译和链接>没有错误。
但 NXP 已弃用另一个工具链 (MCUXpresso 10.0) 的 KDS,该工具链使用 GCC 5.4.1,并且使用完全相同的代码,这次我得到 两个链接器错误:
./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 或 UART3 调用 GetInstance()
,因此从不调用也可以对应GetStorage()
方法。
我的问题是...... C++11 要求我在我的可执行文件中定义所有三种存储方法吗?也就是说,GCC 4.8.4 是否让我摆脱了我不应该做的事情?或者这是我需要切换的一些 GCC 5.4 选项,以允许我从类中删除未使用的静态成员?
如果答案是“无论如何你都必须定义它们”,那么我会定义它们,或者可能设计一些其他方式来允许。如果答案是“那应该没问题”,并且我无法在命令行上设置让 GCC 5.4 执行此操作的选项,那么我将采取下一步并在 NXP 论坛中报告错误。谢谢。
【问题讨论】:
为什么不公开CreateInstance
函数而不是要求用户代码来定义(某些)函数? coliru.stacked-crooked.com/a/5e84854041391a9c
@aschepler - 我喜欢这种模式。但是假设我有一个板对象,我想通过调用GetInstance(2)
来初始化Uart&
引用。我如何保证CreateInstance(2)
将在该对象引用被初始化之前的某个时间被调用?
【参考方案1】:
TL;DR - 我是否需要在一个类中定义所有静态方法,即使这些静态方法从未被应用程序调用
您可能需要定义此类静态方法,即使它们从未被应用程序调用。
但是,一般来说,如果这些函数不是 odr-used(一个定义规则),您可能不需要。对于静态成员函数,这相当于
名称显示为潜在求值表达式的函数
区别很微妙。我希望这能证明这一点:
if(false)
function();
function
永远不会被调用,但会出现在可能求值的表达式中,因此是 odr-used 的,因此必须定义。
我的问题是……C++11 是否要求我在我的可执行文件中定义所有三种存储方法?
是的。它们都出现在可能求值的表达式中,因此是 odr-used,因此必须定义。
我正在使用 ... GCC 4.8.4 开发早期应用程序,并且编译和链接没有错误。
Odr 违规行为未定义,这解释了为什么您在其他工具链中没有收到错误/警告。
【讨论】:
【参考方案2】:[basic.def.odr]
每个程序都应该包含一个对每个非内联函数或变量的定义 该程序在被丢弃的语句(9.4.1)之外;无需诊断。
这是一个相对较新的标准草案,但所有版本都包含类似的声明。
“不需要诊断”子句允许编译器接受您的程序,即使它违反了规则。在无法访问的代码段中使用 odr 正是合理的情况:编译器可能会或可能不会优化掉包含违规调用的死代码。您的程序仍然存在违规,另一个实现可能会拒绝它。
【讨论】:
以上是关于c++ - 是不是必须定义所有静态类方法,即使不使用?的主要内容,如果未能解决你的问题,请参考以下文章
C++中的派生类,可以不定义对象直接调用基类的成员和调用自己的成员函数嘛???