基本类型的C ++类包装器

时间:2013-07-22 17:03:23

标签: c++ types wrapper typedef portability

我见过/使用的许多库都有typedef来提供可移植的,固定大小的变量,例如int8,uint8,int16,uint16等,无论平台如何都是正确的大小(并且c ++ 11自己使用header stdint.h)

最近在我正在编写的小型库中使用二进制文件i / o后,我可以看到以这种方式使用typedef以确保代码可移植的好处。

但是,如果我要输入“namespace :: uint32”而不是使用内置的基本类型,我可能会尽可能地使替换变得有用。因此我正在考虑使用类而不是简单的typedef。

这些包装类将实现所有普通运算符,因此可以与基本类型互换使用。

例如:

int x = 0;
//do stuff

可能会成为

class intWrapper {
//whatever
};

intWrapper = 0;
//do stuff

无需修改“// do stuff”中的任何代码

我正在考虑这种方法而不仅仅是typedef的原因是我已经拥有对基本类型进行操作的函数,例如

std::string numberToString(double toConvert);

std::string numberToHexString(double toConvert);

int intToXSignificantPlaces(const int& number, 
                               unsigned char numberOfSignificantPlaces);

bool numbersAreApproximatelyEqual(float tollerance);
//etc....

从语法上讲,执行以下操作会更好(也更多):

intWrapper.toString();
intWrapper.toHexString();
//etc

它还允许我实现bigint类(int128等),并且那些和较小的(基于基本类型)使用相同的接口。

最后,每个包装器都可以有一个自己的静态实例,叫做max和min,所以int32 :: max和int32 :: min的语法很好。

但是,在我这样做之前,我想要解决一些问题(因为它主要是语法糖,并且这些类型会被使用,因此任何额外的开销都会对性能产生重大影响)。

1)当使用someClass.operator +(),someClass.operator-()等而不是int a + int b时,是否有任何额外的函数调用开销?如果是这样,内联运算符+()会消除所有这些开销吗?

2)所有外部函数都需要基本类型,例如glVertex3f(float,float,float)不能简单地传递3个floatWrapper对象,有没有办法自动使编译器将floatWrapper转换为float?如果是这样,是否会产生性能影响?

3)是否有额外的内存开销?我理解(?)具有继承的类具有某种虚拟表指针,因此使用稍多的内存(或者仅仅用于虚函数?),但假设这些包装类不是从子类继承而不是子类,那么使用类而不是基本类型的任何额外内存使用是否存在?

4)这可能导致其他问题/性能影响吗?

3 个答案:

答案 0 :(得分:11)

  

1)使用someClass.operator +()

时是否有任何额外的函数调用开销

不,如果函数体很小并且在标题中,它将被内联,并且没有开销

  

2)有没有办法自动让编译器将floatWrapper转换为float?

struct floatWrapper {
    floatWrapper(float); //implicit conversion from float
    operator float(); //implicit conversion to float.  
};

同样,如果函数的主体很小并且在标题中,它将被内联,并且没有开销

  

3)是否有额外的内存开销?

如果没有虚拟功能则不行。如果一个类声明或继承任何虚函数,则称为 polymorphic 。如果类不是多态的,则对象不需要包含指向虚函数表的指针。此外,不允许对继承层次结构中的非多态类的指针/引用执行dynamic_cast到指向派生类的指针/引用,因此不需要对象具有某种类型信息。

  

4)这可能导致其他问题/性能影响吗?

表现?否。

此外,请确保实现不将lhs修改为自由函数的二元运算符,并重载它们以支持floatWrapperfloat的所有相关排列。

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。请注意,对于float / double / long double,你需要稍微不同的版本。

答案 1 :(得分:2)

这取决于编译器。如果它有循环或分配,则不太可能内联。

答案 2 :(得分:0)

我认为答案并不完全正确 - 至少对于gcc 4,由于构造函数和操作符调用,我观察到了很大的开销。

以下内容大约是long的两倍:

typedef intWrapperImpl<long> TestWrapper;
//typedef long TestWrapper;

int main (int argc, char** argv) {
    TestWrapper sum(0);
    TestWrapper test(4);

    for (long i = 0; i < 1000000000L; ++i) {
        sum += test;
    }

    cout << sum << "\n";

    return 0;
}

使用不同版本的gcc 4进行优化和不进行优化后,性能没有差异。

在这种情况下,添加

intWrapperImpl& operator+=(const intWrapperImpl & v) {value+=v.value; return *this;}

只有轻微的改善。

使用这样的包装器就好像它们是性能关键代码中的基类型似乎是一个坏主意。在本地使用它们从而一直调用构造函数似乎更糟糕。

这对我来说真是一个惊喜,因为它很容易内联所有内容并对其进行优化,就好像它是一个基类变量一样。

任何进一步的提示将不胜感激!