C++使用泛型导致的膨胀问题

 更新时间:2021年11月12日 10:56:39   作者:临峰不畏  
这篇文章主要介绍了C++使用泛型导致的膨胀,智能家居主机的嵌入式平台上使用C++进行开发。FLASH存储空间有限,这是必须要考虑的因素,一定要重视,下面我们一起进入文章看看详细内容

临峰不畏博主从事C++软件开发多年,由于之前的开发环境都是资源充足的服务器,不用考虑磁盘空间的问题。最近打算在智能家居主机的嵌入式平台上使用C++进行开发。FLASH存储空间有限,这是必须要考虑的因素,一定要重视。

如下定义两个list,元素类型不同:

list<int> l1;
list<string> l2;

如果是用C语来做应该怎么办?它会对应list<int>写一套代码,再对list<string>写一套。每套都有相同的成员函数,只是变量类型各自不同罢了。

下面是list<int>的C语言实现方式:

//! code-1
struct list_int_item {
    int value;
    struct list_int_item *next;
};

struct list_int {
    struct list_int_item *head;
    size_t size;
};

void list_int_insert(struct list_int *p, int value);
int  list_int_sort(struct list_int *p);
bool list_int_empty(struct list_int *p);
...
下面是list<string>的C语言实现方式:
//! code-2
struct list_string_item {
    string value;
    struct list_string_item *next;
};

struct list_string {
    struct list_string_item *head;
    size_t size;
};

void list_string_insert(struct list_int *p, string value);
int  list_string_sort(struct list_int *p);
bool list_string_empty(struct list_int *p);
...

两者之间就是类型的差别。所以很多时间,在C语言中我们就用宏来替代它的类型,

如下:

//! code-3
#define LIST_DECLARE(TYPE) \
    struct list_##TYPE##_item { \
        TYPE## value; \
        struct list_##TYPE##_item *next; \
    }; \
    \
    struct list_##TYPE { \
        struct list_##TYPE##_item *head; \
        size_t size; \
    }; \
    \
    void list_##TYPE##_insert(struct list_##TYPE *p, ##TYPE## value); \
    int  list_##TYPE##_sort(struct list_##TYPE *p); \
    bool list_##TYPE##_empty(struct list_##TYPE *p); \
    ...

然后在头文件中是这样定义list<double>的:

//! code-4

LIST_DECLARE(double)


所以,泛型产生冗余代码是无法避免的,至少用C来做这样的泛型也是无法避免的。
既然无法避免的,那就看看怎么尽可能以避免上述的问题。在《Effective C++》中有一章节专门提到:不要在模板中使用不必要的参数。因为每一个不同的参数编译器都会为之生成一套相应的代码。
如果代码中只有一种数据类型,就算用该类型定义了多个变量,编译器是不是只会生成一套相关的代码?(应该是这样的)。

写个例子对比一下:(省略不必要的代码)

test1.cpp ,里面只有map<int, string>,但定义了m1, m2, m3

//! code-5

    map<int, string> m1;
    map<int, string> m2;
    map<int, string> m3;

    m1.insert(std::make_pair(1, "hello"));
    m2.insert(std::make_pair(1, "hi"));
    m3.insert(std::make_pair(1, "lichunjun"));

test2.cpp ,与test1.cpp相比,里面有三个类型:

//! code-6

    map<int, string> m1;
    map<int, double> m2;
    map<int, int> m3;

    m1.insert(std::make_pair(1, "hello"));
    m2.insert(std::make_pair(1, 1.2));
    m3.insert(std::make_pair(1, 44));


结果,编译出来的可执行文件大小比较:

[hevake_lcj@Hevake tmp]$ ll test1 test2
-rwxrwxr-x. 1 18784 Mar 19 22:01 test1
-rwxrwxr-x. 1 35184 Mar 19 22:03 test2

test2test1大一倍,原因不用多说。

还有一个问题:指针是不是被认为是一个类型?
上面的list<int>list<string>不能共用同一套代码,根据的原因是因为intstring这两种类型在空间大小与赋值的方式上都是不同的。所以,必须生成两套代码来实现。
而指针,不管是什么指针,它们都是一样的。我们可以用void*代表所有的指针类型。

于是我们将上面的代码改改,再测试一下:

//! code-7

    map<int, string*> m1;
    map<int, string*> m2;
    map<int, string*> m3;

    m1.insert(std::make_pair(1, new string("hello")));
    m2.insert(std::make_pair(1, new string("hi")));
    m3.insert(std::make_pair(1, new string("lichunjun")));

//! code-8

    map<int, string*> m1;
    map<int, double*> m2;
    map<int, int*> m3;

    m1.insert(std::make_pair(1, new string("hello")));
    m2.insert(std::make_pair(1, new double(1.2)));
    m3.insert(std::make_pair(1, new int(44)));

结果是这样的:

-rwxrwxr-x. 1 18736 Mar 19 23:05 test1
-rwxrwxr-x. 1 35136 Mar 19 23:05 test2

预期的结果test1test2相差不多,但从结果上看并没有什么优化,结果有点令人失望~

思考:C++有没有什么参数可以优化这个?

如果没有,为了节省空间,我们只能将所有的指针统一定义成void*类型了,在使用时再强制转换。

  

  //! code-9
    map<int, void*> m1;
    map<int, void*> m2;
    map<int, void*> m3;

    m1.insert(std::make_pair(1, new string("hello")));
    m2.insert(std::make_pair(1, new double(1.2)));
    m3.insert(std::make_pair(1, new int(44)));

    cout << *static_cast<string*>(m1[1]) << endl;
    cout << *static_cast<double*>(m2[1]) << endl;
    cout << *static_cast<int*>(m3[1]) << endl;

如上代码是将code-8的基础上,将所有的指定都定义成了void* ,在使用的时候用static_cast进行强制转换成对应的指针类型。
如此得到的代码大小与code-7的比较,只多了16个字节。
但这种做法是很不可取的,必须用void*指针之后,编译器不再对类型进行检查,很容易把类型搞混淆。

最好还是编译器支持指针泛型的优化吧!

到此这篇关于C++使用泛型导致的膨胀问题的文章就介绍到这了,更多相关C++使用泛型导致的膨胀问题内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 使用VS2010创建MFC ActiveX工程项目

    使用VS2010创建MFC ActiveX工程项目

    VS2010开发ActiveX有两种方法,分别是MFC和ATL。MFC开过起来比较简单,但是最终生成的文件比较大,ATL是专门用来开发ActiveX的,但是相对比较难,必须知道很多原理机制和API。咱先从MFC开发ActiveX开始吧。
    2015-06-06
  • 详解C++ 转换的非正式分类

    详解C++ 转换的非正式分类

    C++ 正式分类方法是直接按语法分类,分为:隐式转换和显示转换。这篇文章主要介绍了C++ 转换的非正式分类,需要的朋友可以参考下
    2022-01-01
  • C++设计模式之组合模式(Composite)

    C++设计模式之组合模式(Composite)

    这篇文章主要为大家详细介绍了C++设计模式之组合模式Composite,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-04-04
  • C++类常量和类枚举

    C++类常量和类枚举

    这篇文章主要介绍了C++类常量和类枚举,给类当中定义一些常量,可以给所有类的对象使用,比如说我们在类当中定义一个数组,希望可以定义一个常量,用来初始化数组的长度,那么下面我i吗就来看看过程当如何吧
    2022-01-01
  • C语言中数组的使用详解

    C语言中数组的使用详解

    这篇文章主要为大家介绍了C语言中数组的使用,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2021-12-12
  • 使用C语言判断栈的方向实例

    使用C语言判断栈的方向实例

    下面小编就为大家带来一篇使用C语言判断栈的方向实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧,祝大家游戏愉快哦
    2016-12-12
  • 七大经典排序算法图解

    七大经典排序算法图解

    本文详细讲解了七大经典排序算法,文中通过示例代码介绍的非常详细。对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-12-12
  • C语言判断数是否为素数与素数输出

    C语言判断数是否为素数与素数输出

    大家好,本篇文章主要讲的是C语言判断数是否为素数与素数输出,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2021-12-12
  • Qt实现TCP网络编程

    Qt实现TCP网络编程

    这篇文章主要为大家详细介绍了Qt实现TCP网络编程,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • C语言开发实现贪吃蛇小游戏

    C语言开发实现贪吃蛇小游戏

    这篇文章主要为大家详细介绍了C语言开发实现贪吃蛇小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-10-10

最新评论