• 论C++学习


    很多人把学习C++语言当成学习VC了,我曾经就走过这样的弯路。当学了很长时间的VC后还是得回过头来学习C++语言本身。

    那么学习C++从何处入手呢?
       C++语言相对于C语言来说引入了两个大的新东西,一个就是面向对象(具体来将就是类),另外就是模板技术(模板编程或者叫泛型编程是进来非常流行的技术,在C#中虽然还没有对泛型编程的支持,但是相信在将来也一定会加入这一功能),模板是C++中比较复杂的部分,但是作为一个真正的C++程序员,这部 分很重要。尤其是对C++标准程序库的掌握尤为重要。
      刚开始的时候千万不要直扑VC中的各种向导和设计器。因为依赖开发环境生成的很多代码会把我们搞糊涂,也不利于我们学习C++语言本身。我的建议就是生成一个空的控制台工程,然后自己向里面添加文件。
      如下就是一个简单的控制台程序:
    //robindy/list.cpp
    #include <list>

    #include <iostream>
    using namespace std;
    int main()

    {

    list<char> coll;
    for(char c = 'a'; c <= 'z'; ++c)

    {

    coll.push_back(c);

    }
    list<char>::const_iterator pos;

    for(pos = coll.begin(); pos != coll.end(); ++pos)

    {

    cout << *pos << ' ';

    }

    cout << endl;
    return 0;

    }


    对using namespace std;的解释:

    所谓namespace,是指标识符的各种可见范围。C++标准程序库中的所有标识符都被定义于一个名为std的namespace中。
    由于namespace的概念,使用C++标准程序库的任何标识符时,可以有三种选择:
    1、直接指定标识符。例如std::ostream而不是ostream。完整语句如下:
    std::cout << std::hex << 3.4 << std::endl;
    2、使用using关键字。
    using std::cout;

    using std::endl;
    以上程序可以写成
    cout << std::hex << 3.4 << endl;


    3、最方便的就是使用using namespace std;这样命名空间std内定义的所有标识符都有效(曝光)。就好像它们被声明为全局变量一样。那么以上语句可以如下写:
    cout << hex << 3.4 << endl;
      这段程序用到了标准模板库,在屏幕上打印字符a~z,其中main函数的标记式只有两种是被C++标准委员会接受的,只有以下两种写法是符合C++标准的,是可移植的。即:
    int main()

    {}

    int main(int argc, char* argv[])

    {}


    C++在main()的末尾定义了一个隐式的return 0; 但是在VC中必须显式的写出return语句。
    在BCB中可以不写return而编译通过。
    这段程序中用到了STL中的容器:链表。先是向链表中插入26个字母,然后从遍历链表,输出字符。


      我不建议初学者一开始就从模板库入手,但是我建议应该逐渐地有意识地学习模板库。如我们应该熟悉cout和cin的用法(位于iostream),而减少使用老式的C函数库中printf和scanf等。

    从 控制台入手的好处就是避免我们理解VC中向导等工具生成的其他代码,整个程序的流程很清楚。新手学习VC的一大难点就是搞不清楚整个程序的流程(从那条语 句开始执行,然后从那条语句退出。MFC做了太多封装,掩盖了事实的真相,^_^)。通过控制台我们可以很快地学习C++语言中的很多新特性。如类的封 装、继承和多态等。通过这个时期的学习,要能够掌握三个东西: C++关键字、语法(重点是和面向对象有关的,以及模板)、C++标准程序库(知道怎么使用即可,要彻底掌握需要很多时间和精力)。至于与界面有关的东东(MFC中很大一部分是与界面相关的类,所以我认为MFC很臃肿而无聊!回头看过以前用MFC写的程序,有一种“垃圾”的感觉。)


    自己做个测试:
      写一个控制台程序,在其中体现出类的继承,函数重载,动态多态(通过虚函数实现),数据封装,C++标准库的运用。
      如果你能够轻松搞定,恭喜你,你已经通过C++语言关了。不过C++实在是一个复杂的东东,其中有各种千奇百怪的语法现象,如果没有三、五年的功力,千万不要说自己懂C++。^_^。


    推荐书籍
    《Thinking in C++》(有精力的话,可以直接读原版)
    《Essential C++》


       本来我计划继续说说如何来进入C++世界的,但是琢磨了一下,觉得还是要先解决一个问题:为什么要学习C++?我觉得在软件开发这个行当了里除了为了生 计外,学习新东西都是应该以兴趣为导向的。所以撇开我个人对C++的偏爱,我想为你树立起学习C++的信心和兴趣。当初我对自己学习C++的第一个忠告就 是:不能半途而废!起初是兴趣驱动,后来则是生计所需,最后还是回归到了兴趣。毕竟我从中得到了乐趣,这就足够了。在学一个新东西前,解决动机问题很重要。就如同杀人一样,如果只是突然兴起,那么等尝试了以后,是没有成就感的。革命先烈们为我们作出了很好的榜样,就算我们在学习C++的道路上遇到了太多困惑和痛苦,但是我们毕竟为自己的信念做了努力,我们知道自己在做什么,知道自己在追求什么。


    C++适合做什么样的开发?
       C++是一门广泛用于工业软件研发的大型语言。具有很高的复杂性和解决问题的能力。C++不仅在开发上极具价值,同时在学术界也就有很高的价值。有关C ++的文章应该可以用浩如烟海来形容了吧。C++的世界级经典书籍也是数不胜数。然而,目前开发语言是如此地繁荣,就连微软也在推出了新的开发语言C#。 一个不可否认的现实是,在低阶程序设计领域,C++挤压着C同时也在承受着C的强烈反弹,前段时间看了据说是微软操作系统源代码的东东,其中很多还是C语 言。而在高阶程序设计领域,Java和C#正在不断蚕食着C++的地盘。也许Java和C#的狂潮终将迫使C++回归本位— 回到它有着根本性优势的开发领域:
      低级系统程序设计、高级大规模高性能应用设计、嵌入式程序设计、通用程序设计以及数值科学计 算等。果真如此,我认为这未尝不是一件好事。电力系统软件所要求的高性能和大规模数值计算正是C++所擅长的。就我所接触的南瑞和鲁能,很多涉及到电力系 统计算的软件如PAS等,都是用C++来开发的。在电力系统软件开发这块阵地,C++大有用武之地。C++吸引如此之多的智力投入,以至于这个领域的优秀 作品,包括重量级的软件产品、程序库以及书籍等,数不胜数。在C++之父Bjarne Stroustrup的个人主页上,有一页列出了一些(全部或大部分)使用C++编写的系统、应用程序和库。


    下面是一些例子(摘自荣耀网站):
    o Adobe Systems:所有主要应用程序都使用C++开发而成,比如Photoshop & ImageReady、Illustrator和Acrobat等。
    o Maya:知道“蜘蛛人”、“指环王”的电脑特技是使用什么软件做出来的吗?没错,就是Maya。
    o Amazon.com:使用C++开发大型电子商务软件。
    o Apple:部分重要“零件”采用C++编写而成。
    o AT&T:美国最大的电讯技术提供商,主要产品采用C++开发。
    o Google:Web搜索引擎采用C++编写。
    o IBM:OS/400。
    o Microsoft:以下产品主要采用C++(Visual C++)编写:


    o Windows XP ;Windows NT:NT4、2000; Windows 9x:95、98、Me; Microsoft Office:Word、Excel、Access、PowerPoint、Outlook; Internet Explorer,包括Outlook Express、Visual Studio:Visual C++、Visual Basic、Visual FoxPro .NET Framework类库采用C#编写,但C#编译器自身则使用C++编写而成。Exchange SQL Server FrontPage Project 、所有游戏......
    o KDE:K Desktop Environment(Linux)。
    o Symbian OS:最流行的蜂窝电话OS之一。
    很多新手特别容易会对自己所学习的东东产生疑惑、迷茫。觉得自己学这个东西,花了这么多时间有没有用,会不会过时?这种思想很普遍。


       在一些论坛上经常会看到一些各语言的优劣比较,知道自己所学语言的优劣也好,但是如果一味停留在这个层面就没有用了。任何语言都只是工具而已。重要的是使用工具的人!就我个人的经验来讲,真正处于业界搞开发的人都愿意使用成熟的、为自己所熟知的技术来完成工作。而新手都喜欢用一些比较新的技术来做开发, 而且喜欢追求新奇(从界面很容易看出来,花花绿绿的界面多半出自新手)。其实,安于用一些效率可能低下、扩展性和维护性差的方法来解决问题并不是开发人员的错。他们只是在完成工作而已。但是作为一个真正有上进心的开发人员,我们应该使用更优雅和高效的编程技术,这才是我们逐渐变成编程“大牛”的好习惯。老 是停留在原地,很容易被淘汰的。在软件开发这个行当,尤其如此。无论是对学生,还是一线开发人员,我觉得都不应该产生“书读够了”的感叹!我有时候喜欢将 以前看过的书翻出来再看,每次总能体会到一些新东西。有关C++语言的书籍更是如此,而且我觉得我所起的题目不是很好。为什么?因为我觉得学习语言还只是 新手跨入软件开发“地狱”的第一步,单单学习语言本身是远远不够的,还要学习相关的程序库(C++当然首选是先学习C++标准程序库)、相关的平台技术 (如.NET),说得更远一点,还要锻炼对目标问题的分析、归纳能力等等。工作之前,技术路线自己作主,工作之后,绝大多数程序员将被公司技术路线左右。所以,趁现在还有时间,可以学一些自己感兴趣的。如果想搞软件开发,特别是电力系统软件的开发,学好C++不会令我们失望。当我们进入C++的前门,然后 经过一段黑暗之路,再从后门出来到达光明顶后,我们会体味到“一览众山小”的感觉。


    推荐书籍:
    《编程高手箴言》---------- 梁肇新(用过超级解霸的都应该知道吧,^_^)写的第一本书,其中有几章还是值得一读的。在这本书中,梁告诉我们,学东西要耐心,要耐得住“寂寞”,走自己的路,让别人去“说”吧!
      最近比较忙,原来打算紧扣主题讲讲一些具体的C++语言的细节的,但还是抽不出大段的时间了。所以,现在只能再漫谈一些关于C++的故事了。
       C++源于C语言,还记得很久以前学习C语言的时光(那是一段快乐而充实的时光),可是现在学习C++,并不是在C的基础上加上了类而已,如果这样认 为,我们是耍不好C++的。因此,C++绝不是C的升级或扩充,我们应该把C++当作一门新语言来学习(C++之父Bjarne Stroustrup语)。
      写程序首先希望是程序能正确执行,其次是效率能够被接受,再次就是易于维护。C++是一个难学易用的语言。C++提供了太多可选择的东西,而且使用使用C++来写程序可以有四种思考模式:基于过程、基于对象、面向对象和泛型。我们使用一种语言来写程序,并不意味着就是使用语言本身,换句话说,我们更多的时候是使用程序库在写程序。比如MFC、STL、ATL、VCL等等。其中要使用C++来写出结构 优美、性能卓越、代码简洁、易于维护的代码,首推C++标准程序库。STL对效率做了严格的要求,而且使用STL写出来的程序简洁美观(前段时间我特意贴了一个要求对若干整数进行排序的帖子,其实目的就是用来展示STL的简洁优雅)。一旦习惯使用泛型思维来考虑问题,我们能够充分体会到模板带来的美!对于数值计算来说,C++标准程序库可以充分满足现代化服务和商业计算对数据、信息的即时回应的要求。
      我觉得学好一门语言最重要的 就是实践。也就是多“写”!“工程经验之积累”对已具有一段开发时间的程序员而言,非常重要!只有在不断的积累中,我们才能渐渐体会到C++语言中的一些 背后的东西。对于这点,没有大量程序代码写作经验的菜鸟,也可以借助《Effective C++》先攒一些经验值。《Effective C++》是一本好书!。Meyers的书绝对值得一读,Meyers可以说当今C++社群中数一数二的技术专家。


    推荐网站:
    www.royaloo.com
      以下文字应该是去年所涂鸦而成,主要是关于动态内存分配的,在这里将其重新看了看,觉得还是写得太浅薄了。因为内存是程序运行的“运动场”,对场地的了解程度会直接影响到我们程序运行的流畅度和稳定性。
       C++提供了操作符new来在堆上分配内存,操作符delete来释放内存。有些情况下,我们需要对内存的分配和释放进行更好的控制。许多程序创建和释 放一些重要类的大量的对象,如tree nodes,linked lists links,points,lines,messages,etc.使用通用的内存分配器如new和delete来进行这些对象的分配和释放有时将支配程 序的运行时间和内存需求。

    两方面的因素:通用内存分配操作的运行和空间的耗费以及不同对象大小引起的内存碎片。类使用定制的内存分配器将加快模拟器、编译器和类似程序的执行速度。
       例外一种需要更好的内存控制的情况是:需要在有限资源的情况下长时间不间断运行的程序。实时系统经常需要用最少的耗费来获取有保证的可预期的内存。这也就导致了更好的内存控制的需要。一般来说,这些程序都避免使用动态的内存分配,而使用特殊目的的内存分配器来管理有限资源。
      此外,还有一些情况下由于硬件或系统的要求,需要将对象放在指定的内存位置。这也需要进行定制的内存管理(通过重载new来加以实现)。
      在C++ Release 2.0中,为了满足以上需求,内存管理机制做了相应的修改。主要是引进了operator new [] 和 operator delete []。
    new操作符的作用范围(Scope for operator new Functions)

    操作符(Operator) 范围(Scope)

    ::operator new Global

    class-name::operator new Class
    operator new的第一个参数必须是类型size_t(在STDDEF.H中定义的类型),返回类型为void *。


      当分配内建(built-in)类型的对象、未包含用户自定义的new操作符函数的类对象、任何类型的数组时,使用全局new操作符函数。当在类中自定义new操作符时,分配该类对象的内存时,调用该类的new操作符。如下:
    #include

    #include

    class Blanks

    {

    public:

    Blanks(){}

    void *operator new( size_t stAllocateBlock, char chInit );

    };

    void *Blanks::operator new( size_t stAllocateBlock, char chInit )

    {

    void *pvTemp = malloc( stAllocateBlock );

    if( pvTemp != 0 )

    memset( pvTemp, chInit, stAllocateBlock );

    return pvTemp;

    }

    int main()

    {

    Blanks *a5 = new( 0xa5 ) Blanks;//创建对象Blanks,并且初试化为0xa5

    return a5 != 0;

    }


       new操作符可以重载,而delete却不行。因为等到需要释放的时候,我们所能得到的就是一个指针。而且该指针可能不是原先的对象类型指针(有可能进行了类型转换)。实际上,当使用new获得一个指向一片内存的指针时,在该片内存前有一个指示器(indicator),记录实际分配的内存数量。当调用 delete时,可以获知需要释放的内存大小。数组的释放(Deallocating Arrays):
    void f( )

    {

    X* p1 = new X[10];

    //...

    delete [] X;

    }


      为什么不使用delete [10] X;来释放内存?Bjarne Stroustrup称这种做法容易导致错误,而将记录元素个数的任务放在delete的实现中了。
      至于为什么C++中未内建垃圾收集器(Garbage Collection)的原因,看《C++语言的设计和演化》(En) Bjarne Stroustrup 机械工业出版社(俗称:D&E)可以得到答案。
       此外,C++标准库中提供了一种智能型指针auto_ptr,这种指针可以帮助我们防止“被异常抛出时发生资源泄漏”。但是缺点是该智能型指针不能指向 数组,因为其内部释放内存是通过delete而非delete [] 来进行的。所以,只能使用其来指向一个单个对象。
      模板部分是C++中比较难的部分,也是C++的魅力所在。以下文字是我以前看过的,具体出处不清楚了。今天稍微整理了一下,作为模板介绍的一个单元。

    为什么要使用模板


      对于除类型之外,其余都相同的函数(譬如quicksort),我们一般有3种解决办法。
    1、针对每个不同的类型重复地编写函数实体(C语言的做法):
    int* quicksort(int a[]) {... }

    double* quicksort(double a[]) {... }


    2、使用Object(Java的做法)或者void*
    缺点有两个

    效率问题方面也有问题

    类型检查问题


    3、使用宏预处理机制
    缺点:
      只是愚蠢的文本替换,而且也不会考虑作用域和类型安全。
      然而,应用模板却可以避免这些缺点,我们可以编写:
    template

    T* quicksort(T a[]) {... }


    优点:


      代码简洁优雅,所有参数类型都以T来代替,真正实现了类型无关性。更好的类型安全性,所有的类型检查都是在编译期进行,而且避免使用指针。不存在继承,效率高。(1)没有虚函数;(2)所有的一切工作都是在编译期完成,大大提高运行效率。


    目的:
      告诉编译器如何做出最佳的选择,而且这种选择全部是在编译期完成的。
    模板的机制:
      特化 和 实参演绎
    1、特化


    基本模板:


    template

    class A { // (1)

    void f(T1 a, T2 b);

    }


    局部特化(偏特化):


    template class A { // (2)

    void f(int a, T2 b);

    }


    或者


    template> class A { // (3)

    void f(T a, T b);

    }


    全局特化(显式特化):


    template<>

    class A {

    void f(int a, int b); // (4)

    }


    使用示例:


    A* p1; //将使用(4) ——全局特化

    A* p2; //将使用(3) ——局部特化

    A* p3; //将使用(2) ——局部特化

    A* p4; //将由(1) ——基本模板——生成

    //A


    优点:


    由:全局特化->局部特化->基本模板,这种特化顺序的选择与匹配(重载解析规则)是由编译器自动进行的,无需人工参与。可以根据不同的情况(诸如类型不同,条件不同),给出不同的实现,从而获得更加灵活的针对性。

    可以针对任何变化,改善了程序的扩展性。


    2 实参演绎


    T const& f(T const& a, T const& b)

    {

    return a + b; //1处

    }

    int g = f(1,2);


    实际上f(1,2)要匹配的函数是int const& f(int const&,int const&);

    而这个函数又是怎么来的呢?


    优点:


      再也无需提供一对尖括号和里面的实参,诸如f(1,2),有了实参演绎,我们就可以写成f(1,2)。


    模板的应用


    1、标准库(STL)——到处都是模板代码


    标准库=算法+容器+迭代器

    如list /


    2、类型无关性(T)


    3、trait和policy


    (1)trait: 主要用到了许多typedef和特化,指定的是一种特性。


    // traits/accumtraits3.hpp

    template

    lass AccumulationTraits;

    c template<>

    class AccumulationTraits {

    public:

    typedef int AccT;

    static AccT const zero = 0;

    };

    template<>

    class AccumulationTraits {

    public:

    typedef int AccT;

    static AccT const zero = 0;

    };

    template<>

    class AccumulationTraits {

    public:

    typedef long AccT;

    static AccT const zero = 0;

    };


    (2)policy:通常表现为某个函数,指定的是一种行为


    class SumPolicy {

    public:

    template

    static void accumulate (T1& total, T2 const & value) {

    total += value;

    }

    };


    (3)trait和policy的用法:


    template >

    class Accum {

    public:

    typedef typename Traits::AccT AccT;

    static AccT accum (T const* beg, T const* end) {

    AccT total = Traits::zero();

    while (beg != end) {

    Policy::accumulate(total, *beg);

    ++beg;

    }

    return total;

    }

    };


    4、Metaprogramming


    编译期计算、递归的思想


    5、新形式的设计模板


    (第三、第四、第五点以后再详细介绍)


    《C++ Templates中文版》的具体介绍


    第1部分介绍了模板的基本概念,以教程的风格来介绍这些基本概念。

    第2部分阐述了模板的语言细节,可以作为一本基于模板的构造的参考手册。

    第3部分介绍了C++模板所支持的基本设计技术,范围覆盖从微小的概念一直延伸到复杂的用法;一些技术在别的书籍都没有出现过。

    第4部分基于前两部分,深入讨论了各种使用模板的普通应用程序。


    VC要学多久?


    学一个月,可以用VC写一些小程序自己玩玩

    学两个月,可以用VC写像样点的东西在周围人面前炫炫

    学三个月,可以用VC给老板开始干活了

    学六个月,开始重头去学C++

    学一年后,决定要不要继续,if(继续) 学习MFC、ATL、STL、C#、BCB、Network、Databa

    se、Algorithm... else 开始就是个错误

    学三年后,学会怎么来用编程语言来解决问题,VC、BCB等都只是解决问题的工具。这时候

    你如果还在学C++,你可以从事软件开发这个很有“前途”的职业了。

     

    程序设计中的一些感悟
    (罗恩发表于2002-10-29 8:16:01)

    1)学习应该从基础打起,不要一开始就尝试最高深的技术。
    2)每看一本书,不要说这章我以前学习过了,也掌握的很好,因此我可以跳过这一章看更重要的了。
    3)对于作业,遇到不会的尽量不要立刻向别人请教。如果实在解决不了的问题,可以先完成你会的,然后把一些特别的难点提炼出来,向高手请教。
    3)不要指望书本和行家能帮你解决一切问题,因为并不是所有问题都能由别人教给你。

    4)向别人请教问题应该把问题说明白。对于错误提示信息应该原样提供出来,不要按自己理解的信息提供。因为既然你自己做不了,说明你理解一般都有问题。
    5)问问题最好能带代码。
    6)不要说“编译通过,可是运行时...",因为编译错误和运行错误可能根本没有关系。一般来说,编译是语法问题,而运行是逻辑问题。
    7) 书看千遍不如做程序一遍,应该尽量尝试去写程序。
    8)做程序千个不如做好程序一个。应该尽量完善你现在做的程序,而不要不断开新的计划,而每个计划都虎头蛇尾。


    9)要想到你不是一个人写程序,而是和大家一起写程序。
    10)高深的技巧虽然显示了高深的本领,但是对于合作往往是有害的,应该尽量写出简单易读的代码。
    11)编制程序应该尽量做到自注释,即代码本身一读就懂,好象自己在说明自己的逻辑一样。
    12)复杂的代码如果实在做不到自注释,应该给出适量的注释。
    13)注释在修改代码的时候应该相应修改,不能用陈旧的注释去误导别人。
    14)代码应该尽量可重用,相同功能的代码应该由相同的函数完成,重要函数应该给出调试信息,以便调试时及早发现问题。
    15)应该尽量写小函数,每个函数尽量不要超过40行或者更少。这样不用滚动屏幕也许就可以读完整个函数。
    16)对于switch语句,尽量不要有过多的分支,如果分支太多,可以考虑用跳转表。
    17)尽量少使用一些有争议的语句,如goto和三目运算符,既然有争议,它肯定有一定的缺点。
    18)对于goto,许多工程师技术高到可以合理使用,而不至于导致问题。但是你的程序并不一定给你同水平的人看和修改,他们可不能保证合理的读和修改这些相关代码。
    19)代码编写时应该有一定的格式,其基本要求是对理解代码有一定帮助。
    20)如果数据是多个模块共有的,应该提供一个封装的类来管理它,并提供一个合适的接口给各个模块。这样,如果数据内容有重大修改,则只要接口不变,基本上可以保证程序不要很复杂的修改。
    21)应该尽量考虑到数据的并发控制。
    22)数据的并发控制应该封装在接口内,而不要暴露给其他模块,这样可以减少因为并发原因导致的程序死锁。
    23)数据本身结构不可以太复杂。应该尽量把不相关的数据分割成为两组数据。
    24)对于数据量比较大的情况,应该考虑数据库。
    25)数据库接口应该采用标准ODBC或者ADO接口,尽量不要根据实际数据库DBMS提供的接口来处理,因为你可能在实际使用中更换DBMS。
    26)小的数据可以考虑文件,文件路径应该必须设计成相对路径。
    27)在一个函数中,应该尽量打开文件后使用完后立刻关闭,这样其他程序可能使用文件。
    28)不要尝试把文件全部读到内存中,应该分次处理大文件。
    29)编写程序应该提供相关的测试程序,以提供测试手段。
    30)应该考虑代码、函数的使用情况,不要超越函数可以使用的范围使用之。
    随便写了这么点,呵呵,应该是比较凌乱的,也不完全,希望大家不要见笑。

  • 相关阅读:
    九度-剑指Offer
    Fiddler如何模拟弱网环境进行测试
    登录功能测试点
    【转】移动应用崩溃日志收集工具对比
    【转】高扩展性网站的50条原则
    互联网常见架构接口压测性能分析及调优手段建议
    安装JDK时提示 IllegalArgumentException:Invalid characters in hostname的解决方法
    JAVA+Maven+TestNG搭建接口测试框架及实例
    Jmeter(二)Jmeter目录介绍
    性能测试-Gatling(一)
  • 原文地址:https://www.cnblogs.com/kex1n/p/1647871.html
Copyright © 2020-2023  润新知