C和C++的面向对象专题(3)——C++中的不优雅特性

本专栏文章列表

一、何为面向对象

二、C语言也能实现面向对象

三、C++中的不优雅特性

四、解决封装,避免接口

五、合理使用模板,避免代码冗余

六、C++也能反射

七、单例模式解决静态成员对象和全局对象的构造顺序难题

八、更为高级的预处理器PHP

三、C++中的不优雅特性

今天来说一说C++中不优雅的一些问题,C++虽然是面向对象的设计语言,但也有很多缺陷和弊病,我们将会讨论如何通过良好的设计解决这些问题。

C++编译缓慢

C++编译慢已经成为了业界共识,一个大型C++项目甚至要用专用的服务器编译好久才能完成,Java和.net作为大型开发平台, 却也没发现编译如此缓慢的问题,那么究竟是什么,导致了C++编译难的问题呢?

模板的纠结

C++中模板有个很神奇的问题,就是实现和声明都必须被使用者引用,这段模板代码才有效,也就是说,模板是在编译时展开的代码生成机制。

我们不妨做个实验,这是类的声明:

template<class T>
class CObject
{
public:
    CObject(T k) {obj = k;}
    ~CObject() {}
    T getObj();
private:
    T obj;
};

下面是类的实现:

#include "CObject.h"

template<class T>
T CObject<T>::getObj(){
    return this->obj;
}

主函数中调用:

#include <cstdio>
#include "CObject.h"

using namespace std;

int main(){
    CObject<int> Obj(10);
    int k = Obj.getObj();
    printf("%d\n", k);
    return 0;
}

一切看起来是那么的顺利,但是!我的电脑给我显示如下错误信息:

Scanning dependencies of target template_test
[ 50%] Building CXX object CMakeFiles/template_test.dir/src/CObject.cpp.o
[100%] Building CXX object CMakeFiles/template_test.dir/src/main.cpp.o
Linking CXX executable template_test
CMakeFiles/template_test.dir/src/main.cpp.o:在函数‘main’中:
main.cpp:(.text+0x22):对‘CObject<int>::getObj()’未定义的引用
collect2: error: ld returned 1 exit status
make[2]: *** [template_test] 错误 1
make[1]: *** [CMakeFiles/template_test.dir/all] 错误 2
make: *** [all] 错误 2

链接器告诉我,我们找不到一个叫做‘CObject::getObj()’的函数,恩?为何,我们不是将类实现链接进来了么?

如果你这样想就错了,上网查找解决方案,得到的回复居然是这样:

#include "CObject.h" => #include "CObject.cpp"

omg,那我还不如把两个文件写成一个hpp来的方便呢,其实C++也是推荐你这样做的,理由就是——模板是编译时,在用到的时候进行代码展开得到的

如果不这样做,链接器是不会找到对应的代码的。

那么也找到了很多大型工程如boost库,为何编译缓慢的直接原因,大量的模板展开消耗了巨大的资源,而且模板展开是很不利于代码复用的,同样的算法,换一种类型,必须全部编译,生成新的代码,并且这类模板生成的代码,不能提前编译成二进制库,这样的结果就是,项目哪里改动一点,好多文件重复编译,造成编译十分缓慢。

封装的问题

C++的类并没有很好的将代码封起来,这和上次讲到的GObject对比可以发现,C++的私有变量是一同放置在类的声明中,而我们知道,一个类的声明,是会被很多其他类引用的。

那么,思考我们的C++编译过程,很多类都引用了一个.h文件,那么这个.h文件一旦发生更改,那么所有引用这个文件的cpp文件都将被触发重复编译,而我们在实现一个类时,对类的成员函数小修小补是很平常的,但由于封装的不彻底,那么我们的项目又将被反复编译,带来编译的速度缓慢。

而且,如果是库的话,那么私有成员的更新甚至还会影响用户使用,非常麻烦。

例如下面这段代码:

class Test {
public:
    Test();
    ~Test();

    void Show();

private:
    std::string message;
    int pointer;
    void formatMessage(std::string&);
};

很明显,一般的C++类,私有成员都会比公开成员多,那么私有成员修改一点,哪怕只是一不小心多了个空格,都会带来这个文件的更新,触发makefile的重编译,带来了低效率。

缺乏反射机制

最新的C++11,引入了众多的新特性,包括好用的auto关键字以及模板元编程特性等,但这些,还是不能弥补反射机制缺失带来的影响。反射是对象串行化、GUI界面事件响应和根据数据动态调用代码等技术的核心,缺乏反射机制,会使得C++很多地方十分的不便。

很多大型软件,如firefox,在实现中,往往搭建了反射框架,供系统使用。但由于C++本身语法的问题,缺乏反射依旧会使得类书写变得困难。

跨平台困难

C++的跨平台性真的不好,甚至很多编译器上都会出现匪夷所思的问题,例如在不同平台上,基本类型的大小会随CPU字长而变化,如果有跨平台需求的软件,最好使用跨平台定义的类型。

C++的结构体中数据往往有内存对齐的问题,有些编译器还能通过编译器指令对其设置,这些问题最好还是能避开就避开。

跨平台时,还应小心异常处理的代码,因为有些版本的C++编译器对抛出的异常规格并不很遵守规范。

另外,不同平台的宽字符集也是大问题,往往并不能轻松统一,另外MinGW里貌似就没有宽字符- -

时间: 2024-10-15 02:56:00

C和C++的面向对象专题(3)——C++中的不优雅特性的相关文章

C和C++的面向对象专题(8)——更为高级的预处理器PHP

本专栏文章列表 一.何为面向对象 二.C语言也能实现面向对象 三.C++中的不优雅特性 四.解决封装,避免接口 五.合理使用模板,避免代码冗余 六.C++也能反射 七.单例模式解决静态成员对象和全局对象的构造顺序难题 八.更为高级的预处理器PHP 八.更为高级的预处理器PHP C++的宏在某些情况下非常难用,例如将代码展开成为这样: Macro( A, B, C, D ) => func("A", A); func("B", B); func("C&

C和C++的面向对象专题(1)——何为面向对象

题记: 面向对象是一种思想,而不是一门语言 我们上哪去找对象,都面向对象去了 本专栏文章列表 一.何为面向对象 二.C语言也能实现面向对象 三.C++中的不优雅特性 四.解决封装,避免接口 五.合理使用模板,避免代码冗余 六.C++也能反射 七.单例模式解决静态成员对象和全局对象的构造顺序难题 八.更为高级的预处理器PHP 一.何为面向对象 现在学软件开发,都讲面向对象的编程模型,其实也很简单.用一句话来总结,面向对象就是将方法和方法的属性整合在一起,让每个方法引用的属性值尽可能在对象内部,对外

C和C++的面向对象专题(2)——C语言也能实现面向对象

本专栏文章列表 一.何为面向对象 二.C语言也能实现面向对象 三.C++中的不优雅特性 四.解决封装,避免接口 五.合理使用模板,避免代码冗余 六.C++也能反射 七.单例模式解决静态成员对象和全局对象的构造顺序难题 八.更为高级的预处理器PHP 二.C语言也能实现面向对象 今天要为大家介绍C语言的面向对象设计方法,正如题记上面所说,面向对象是一种思想,而并非是一种语言,我们将会介绍C语言实现的面向对象开发方式. 简单实用的C语言面向对象设计思路 众所周知,C++中的面向对象十分方便,但在C中,

C和C++的面向对象专题(9)——Gtkmm的最佳实践

本专栏文章列表 一.何为面向对象 二.C语言也能实现面向对象 三.C++中的不优雅特性 四.解决封装,避免接口 五.合理使用模板,避免代码冗余 六.C++也能反射 七.单例模式解决静态成员对象和全局对象的构造顺序难题 八.更为高级的预处理器PHP 九.Gtkmm的最佳实践 九.Gtkmm的最佳实践 在跨平台的gui开发中,Qt一直是非常受欢迎的GUI开发框架,但Qt一个是依赖反射,需要特殊的预处理步骤,一个是库太过庞大,这就造成了一些不便的地方.今天介绍给大家的是Gtk库的C++绑定,Gtkmm

C和C++的面向对象专题(7)——单例模式解决静态成员对象和全局对象的构造顺序难题

本专栏文章列表 一.何为面向对象 二.C语言也能实现面向对象 三.C++中的不优雅特性 四.解决封装,避免接口 五.合理使用模板,避免代码冗余 六.C++也能反射 七.单例模式解决静态成员对象和全局对象的构造顺序难题 八.更为高级的预处理器PHP 七.单例模式解决静态成员对象和全局对象的构造顺序难题 上回书说道,我们的程序有一个隐藏的漏洞,如果ClassRegister这个类所在的.o文件,如果在所有.o文件中是第一个被链接的的,那么就不会出问题. 这么说太抽象了,让我们画个图表 ClassRe

C和C++的面向对象专题(6)——C++也能反射

本专栏文章列表 一.何为面向对象 二.C语言也能实现面向对象 三.C++中的不优雅特性 四.解决封装,避免接口 五.合理使用模板,避免代码冗余 六.C++也能反射 七.单例模式解决静态成员对象和全局对象的构造顺序难题 八.更为高级的预处理器PHP 六.C++也能反射 今天我们来探讨C++的反射问题,缺乏反射机制一直是C++的大问题,很多系统的设计时,需要根据外部资源文件的定义,动态的调用内部的函数和接口,如果没有反射,将很难将外部的数据,转换为内部的方法. Java和.net的反射机制很容易实现

C和C++的面向对象专题(5)——合理使用模板,避免代码冗余

本专栏文章列表 一.何为面向对象 二.C语言也能实现面向对象 三.C++中的不优雅特性 四.解决封装,避免接口 五.合理使用模板,避免代码冗余 六.C++也能反射 七.单例模式解决静态成员对象和全局对象的构造顺序难题 八.更为高级的预处理器PHP 五.合理使用模板,避免代码冗余 下面我们来讨论一下,如何解决模板的不易封装的问题. 我们提供这样一种思路,对于链表一类的通用类型,我们尽量采取强制类型转换的方式,尽量避免模板的滥用. 同样,我们应该避免对结构体的直接存储,尽量使用类似java的指针传递

C和C++的面向对象专题(4)——解决封装,避免接口

本专栏文章列表 一.何为面向对象 二.C语言也能实现面向对象 三.C++中的不优雅特性 四.解决封装,避免接口 五.合理使用模板,避免代码冗余 六.C++也能反射 七.单例模式解决静态成员对象和全局对象的构造顺序难题 八.更为高级的预处理器PHP 四.解决封装,避免接口 恩,今天我们来讨论,如何通过设计,解决C++中的不优雅特性,改进项目的结构,改善编译速度. 上次我们提到,如果一个类的封装不好,容易导致种种不便,那么如何设计能够避免这种现象呢? class test { public: voi

面向对象(子父类中变量、函数、构造函数的特点)

/** * Created by 刘朋程 博客园 on 2014-07-15. */ class fu {     int num = 4 ;               //定义父类的变量num = 4     fu()                        //定义父类的构造函数     {         System.out.println("fu run");     }     void speak()                //定义父类的方法     {