问题描述
我见过/使用过的许多库都有 typedef 来提供可移植的、固定大小的变量,例如 int8、uint8、int16、uint16 等,无论平台如何,它们的大小都是正确的(而 c++11 本身使用标头 stdint.h)
最近在我正在编写的一个小型库中使用二进制文件 i/o 后,我可以看到以这种方式使用 typedef 以确保代码可移植的好处.
但是,如果我要输入namespace::uint32"而不是使用内置的基本类型遇到麻烦,我也可以使替换尽可能有用.因此我正在考虑使用类而不是简单的 typedef.
这些包装类将实现所有普通运算符,因此可以与基本类型互换使用.
例如:
int x = 0;//做东西
可能变成
class intWrapper {//任何};intWrapper = 0;//做东西
无需修改//do stuff"中的任何代码
我考虑这种方法而不只是 typedef 的原因是我已经有了对基本类型进行操作的函数,例如
std::string numberToString(double toConvert);std::string numberToHexString(double toConvert);int intToXSignificantPlaces(const int& number,无符号字符 numberOfSignificantPlaces);bool numbersAreApproximatelyEqual(float tolerance);//等等....
从语法上讲,执行以下操作会更好(也更糟糕):
intWrapper.toString();intWrapper.toHexString();//等等
它还允许我实现 bigint 类(int128 等)并让这些类和较小的类(基于基本类型)使用相同的接口.
最后,每个包装器都可以有一个自己的静态实例,称为 max 和 min,所以 int32::max 和 int32::min 的好语法是可能的.
但是,在执行此操作之前,我想解决一些问题(因为它主要是语法糖,并且这些类型会被使用,因此通常会使用任何额外的开销可能会对性能产生重大影响).
1) 在使用 someClass.operator+()、someClass.operator-() 等时,是否有任何额外的函数调用开销,而不是仅使用 int a + int b?如果是这样,内联 operator+() 会消除所有这些开销吗?
2) 所有外部函数都需要原始类型,例如 glVertex3f(float, float, float) 不能简单地传递 3 个 floatWrapper 对象,有没有办法让编译器自动将 floatWrapper 转换为 float?如果是,是否会影响性能?
3) 是否有额外的内存开销?我理解(?)具有继承的类具有某种虚拟表指针,因此使用稍微更多的内存(或者只是用于虚拟函数?),但假设这些包装类不是继承自/不是子类,则没有使用类而不是基本类型是否有任何额外的内存使用?
4) 这是否会导致任何其他问题/性能影响?
1) 使用 someClass.operator+() 时是否有任何额外的函数调用开销
不,如果函数体很小并且在头中,它将被内联,并且没有开销
<块引用>2) 有没有办法让编译器自动将 floatWrapper 转换为浮点数?
struct floatWrapper {浮动包装器(浮动);//从浮点数隐式转换运算符浮动();//隐式转换为浮点数.};
同样,如果函数体很小并且在头中,它将被内联,并且没有开销
<块引用>3) 是否有额外的内存开销?
如果没有虚函数就不行.如果一个类声明或继承了任何虚函数,它就被称为多态.如果类不是多态的,则对象不需要包含指向虚函数表的指针.此外,不允许在继承层次结构中将指向非多态类的指针/引用动态转换为指向派生类的指针/引用,因此对象不需要具有某种类型信息.><块引用>
4) 这是否会导致任何其他问题/性能影响?
性能?号
另外,确保将不修改 lhs 的二元运算符实现为自由函数,并重载它们以支持 floatWrapper
和 float
的所有相关排列.
struct floatWrapper {显式 floatWrapper(float);运算符浮动();//隐式转换为浮点数.floatWrapper 运算符-=(float);};floatWrapper 运算符-(floatWrapper lhs, floatWrapper rhs){返回 lhs-=rhs;}floatWrapper 运算符-(float lhs, floatWrapper rhs){return floatWrapper(lhs)-=rhs;}floatWrapper 运算符-(floatWrapper lhs, float rhs){返回 lhs-=rhs;}
他的尝试.请注意,您需要一个稍微不同的 float/double/long double 版本.
Many libraries I have seen/used have typedefs to provide portable, fixed size variables, eg int8, uint8, int16, uint16, etc which will be the correct size regardless of platform (and c++11 does it itself with the header stdint.h)
After recently using binary file i/o in a small library I'm writing I can see the benefit of using typedefs in this way to ensure the code is portable.
However, if I'm going to the trouble of typing "namespace::uint32" rather than using built in fundamental types, I may as well make the replacement as useful as possible. Therefore I am considering using classes instead of simple typedefs.
These wrapper classes would implement all normal operators so could be used interchangeably with the fundamental type.
Eg:
int x = 0;
//do stuff
could become
class intWrapper {
//whatever
};
intWrapper = 0;
//do stuff
without having to modify any code in "//do stuff"
The reason I'm considering this approach as opposed to just typedefs is the fact I already have functions that operate on fundamental types, eg
std::string numberToString(double toConvert);
std::string numberToHexString(double toConvert);
int intToXSignificantPlaces(const int& number,
unsigned char numberOfSignificantPlaces);
bool numbersAreApproximatelyEqual(float tollerance);
//etc....
Syntactically it would be nicer (and more oop) to do the following:
intWrapper.toString();
intWrapper.toHexString();
//etc
Also it would allow me to implement bigint classes (int128, etc) and have those and the smaller ones (based on fundamental types) use identical interfaces.
Finally each wrapper could have a static instance of itself called max and min, so the nice syntax of int32::max and int32::min would be possible.
However, I have a few concerns that I would like to address before doing this (since it is mostly syntactical sugar and these types would be used so commonly any extra overhead could have a significant performance impact).
1) Is there any additional function calling overhead when using someClass.operator+(), someClass.operator-() etc over just int a + int b? If so, would inlining operator+() eliminate ALL this overhead?
2) All external functions require the primitive type, eg glVertex3f(float, float, float) could not simply be passed 3 floatWrapper objects, is there a way to automatically make the compiler cast the floatWrapper to a float? If so, are there performance impacts?
3) Is there any additional memory overhead? I understand(?) that classes with inheritance have some sort of virtual table pointer and so use slightly more memory (or is that just for virtual functions?), but assuming these wrapper classes are not inherited from/are not child classes there isn't any additional memory use using classes instead of fundamental types, is there?
4) Are there any other problems / performance impacts this could cause?
1) Is there any additional function calling overhead when using someClass.operator+()
No, if the function body is small and in the header, it will be inlined, and have no overhead
2) Is there a way to automatically make the compiler cast the floatWrapper to a float?
struct floatWrapper {
floatWrapper(float); //implicit conversion from float
operator float(); //implicit conversion to float.
};
Again, if the body of the function is small and in the header, it will be inlined, and have no overhead
3) Is there any additional memory overhead?
not if there's no virtual functions. A class is called polymorphic if it declares or inherits any virtual functions. If a class is not polymorphic, the objects do not need to include a pointer to a virtual function table. Moreover, performing dynamic_cast of a pointer/reference to a non-polymorphic class down the inheritance hierarchy to a pointer/reference to a derived class is not allowed, so there is no need for the objects to have some kind of type information.
4) Are there any other problems / performance impacts this could cause?
performance? No.
Also, be sure to implement binary operators that don't modify the lhs as free functions, and overload them to support all relevant permutations of floatWrapper
and float
.
struct floatWrapper {
explicit floatWrapper(float);
operator float(); //implicit conversion to float.
floatWrapper operator-=(float);
};
floatWrapper operator-(floatWrapper lhs, floatWrapper rhs)
{return lhs-=rhs;}
floatWrapper operator-(float lhs, floatWrapper rhs)
{return floatWrapper(lhs)-=rhs;}
floatWrapper operator-(floatWrapper lhs, float rhs)
{return lhs-=rhs;}
Here's my attempt at such a thing. Note you'll need a slightly different version for float/double/long double.
这篇关于围绕基本类型的 C++ 类包装器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!