zoukankan      html  css  js  c++  java
  • C与C++的编程风格区别

    c和c++都是在实践中发展起来的语言。实用性极强。c是与UNIX/Linux的发展相辅相成的。而C++是B.S为了摆脱c与硬件以及底层过于紧密的苦恼,而开发的语言。C++可以说就是c语言的超集。任何c语言的程序理论上都应当是合法的C++程序。

      C语言的特点:过程性编程和结构化编程。函数作为编程的主要载体和任务模块。一方面用for,while,if-else为代表的分支来规划程序结构,另一方面采用自顶向下编程思想。大任务分为小任务,小任务再往下分,表现为,函数套函数。结构清晰明了。

      C++语言特点:1.过程性编程 ----- 函数或者类中的方法设计与开发仍然占据重要地位。

             2.面向对象编程(OOP)------以封装,继承,多态等思想为代表。主要的工作在于类的设计和实例(对象)的应用。

             3.通用编程(generic programming)。------------以模板template为代表。。。实际上到现在自己也没怎么学会这个。要加油了。

    一开始,C++的规范并不太一致,C则以K&R C为通用标准。后来ANSI/ISO制定了ANSI/ISO C++标准,同时制订了ANSI/ISO C标准,定义了两种语言都使用的标准C库。。从此,两种语言有了官方的标准并且不断由官方发布更新版本。

    在官方标准中,C语言引进了函数原型const类型限定符,并且支持了//注释格式。以及long long等新的整型。

    今年寒假,考完研,就找了一本C++和一本C的教材,在毕(shi)业(ye)之前好好温习一下两种语言。两种语言有很多相似之处。下面总结(zhao chao)一下两种语言的相似之处.

    第一条:include 的头文件。

      C语言的传统是使用扩展名.h,例如math.h。C++的定位之一是C语言的拓展,所以起初C++也是以.h作为头文件后缀,而且有些C的头文件直接当作C++的头文件。

    但是后来C++的标准发生改变:头文件不再使用拓展名,来自C语言的头文件,在头文件名前加上前缀c。例如:math.h --》cmath和iostream.h --》iostream 。同时,这种改变也意味着一些新的特性,比如namespace,等被加入。所以一些老旧的编译器会支持#include<iostream.h>但是不支持#include<iostream>。

    头文件命名约定  
    头文件类型 约定 范例 说明
    C++旧式风格 以.h结尾 iostream.h C++程序可以使用
    C旧式风格 以.h结尾 math.h C,C++程序可以使用
    C++新式风格 没有拓展名 iostream C++程序可以使用,使用namespace std
    转换后的C 加上前缀c,没有拓展名 cmath C++程序可以使用,可以使用不是c的特性

    第二条:main函数

      经典C函数头: main() //original C style, omitting int

    在C语言中,省略返回类型就是默认函数类型为int。。。但是,C++逐步淘汰这种用法,所以不建议省略 int 返回类型。而且不建议使用 void main()。因为部分系统可能不支持。

    此外int main(void)在C和C++中都表示函数不接受任何参数。而int main()在C++中与使用void相同作用,在C中则意味着对是否接受参数保持沉默。(好吧,什么叫保持沉默。。。。)

    第三条:注释风格

    /*-----*/是C的注释风格,//是C++的注释风格。C99之后,C支持了//。两者在两种语言中均可混用。但是应尽量保持风格统一。

    同时,应尽量使用C++注释风格,因为它不涉及到结尾符号与起始符号的匹配。有些人写了/* 就容易忘记写 */

    第四条:const与 #define 定义符号常量。

    编译过程中,先将源程序先交给预处理器(the preprocessor)作相应的处理。然后再编译。不是边编译,边处理。比如#include 将整个头文件替换到#include 这个语句处。#define X Y是先在源程序中查找独立的标记X,将其替换为Y。。。替换完之后再编译。

    c中常用#define MAX 100来定义常量。C++支持这种C风格,但是它提供了更适合的const int MAX = 100;

    在C中,const定义常量,初始值可以为常量数字或者常量数字参与的表达式,但是不能是const常量或者const常量参与的表达式。数组大小亦如此。

    C++中,const常量的初始值和数组大小的初始值均可以为const常量参与的表达式。

    #define LIMIT 20
    const int LIM = 20;
    static int data1[LIMIT];			//C++ Valid, C Valid
    static int data2[LIM];			//C++ Valid, C Invalid
    const int LIM2 		= 2 * LIMIT;	//C++ Valid, C Valid
    const int LIM3		= 2 * LIM; 	//C++ Valid, C Invalid

    在头文件中必须使用#define MAX。因为const int MAX = 100.是一种定义。一个变量只能被定义一次。

    此外,const常量具有作用域规则,如for循环内部的const常量,在for外失效。但是#define的作用域是从define开始到文件结尾。

    使用const要注意以下规则:

    1. 创建常量的通用格式: 注意,声明常量时必须要初始化。而且初始化之后,常量不能进行改变。
      const type name = value;
    2. 在头文件中尽量不要使用const,而应该使用#define。
    3. const可以根据C++的作用域规则将定义限定在特定的函数或者文件中。而#define则在其所在文件范围内均可见
    4. 如果数据类型本身不是指针,则可以将const数据或非const数据的地址赋给指向const的指针,但只能将非const数据的地址赋给非const指针。
    5. 由第四条可知,我们要尽可能使用const,尤其是指针作为函数的参数,而函数又不会通过指针修改指针指向的数据时,这点在类的设计方面更加凸显。

      原因有两条:其一:这样可以避免由于无意间修改数据而导致的编程错误;其二:使用const使得函数能够处理const和非const实参,否则将只能接受非const。所以如果条件允许,则应将指针形参声明为指向const的指针。

    第五条:bool类型

    此处暂留,等看完if-else之后综合比较。主要观点就是经典C中没有bool类型,C++中有bool。但是自从C99之后,C可以支持bool型了。

    C语言的布尔类型

    第六条:强制转换

    强制转换的通用格式:

    (typename)value //C-style, both supporting
    typename(value) //C++ -style, only C++ supporting

    static_cast<typename> (value) //another operator for casting in C++..

    第二种C++风格的想法是强制转换类似与函数调用,所以借鉴函数调用格式。value类似与传入参数,typename类似与函数名。但是,一和二一般采用C风格。。第三个是C++独有的强制类型转换操作符。

    第七条:指针

    1,在C中,将指针视为无符号整数,注意并不是int类型。。具体格式看具体编译器和操作系统的实现。而C++将指针(地址)视为一种独立的类型。所以:

    //字符串常量在C和C++中编译时,表示的是字符串所在的内存地址。
    
    char fish[] = "Bubbles"//字符串常量初始化数组。实际上将const char * 赋值给char* 类型的fish。,alright,no pa。
    char fish = “Bubbles”;
    //这句话在C中能够编译通过,但是会发出警告。将指针赋值给整型变量。默认启用。
    //但是在C++中。这句话是错误的。无法编译通错,会报错:不能将const char * 赋值给char类型变量。因为他们是不同的类型。
    int * a = 100;     //在C语言中将整数赋值给指针,可以编译通过,但是会发出警告。
    int * a = 100;     //在C++中,则不可以,因为int 和int *是两种类型。
    int * a = (int *)100;//也可以通过强制类型转换。但是,这样做十分危险。。

    ps,在C/C++中将NULL初始化指针。要养成这样的好习惯。。不然指针未被赋值时,其内部存储的数据是任意的,无法预测的。十分危险。

    2,C,C++对待void类型指针是不同的。在两种语言中,都可以将一个指向任意类型的指针赋值给void *。但是 ,在将一个void *赋值给一个指向具体数据类型的指针时,C++需要一次强制类型转换。C中,这种强制转换是可选的。但是考虑到强制类型转换更能表达指针意义,建议使用强制类型转换。

    第八条:string类和C-风格字符串

    string类是ISO/ANSI C++ 添加的。使用时要#include。要使用处理C-风格字符串的函数,如strlen,strcmp,要#include 或者#include。。。其余的。。属于OOP和C语言基本功的问题了。。。

    另外,C++很早就有istream类,而string类是后期出现的。所以cin>>和cin.getline()都没有考虑针对string类的重载。。所以通过重载>>和getline()为string类和istream类的友元函数实现cin>>str, getline(cin, str)。。不能用cin.getline(string, int)。。

    第九条结构体:struct

    记住所有C支持的struct的特性,C++都支持。但是C++具有很多独有的特性。而且这种特性是类(class)相似的。

    1. 在C中,声明结构体变量时struct关键字不能省略。但是在C++中可以。C++这种变化是强调结构声明定义了一种新类型。类比类(class),声明类变量时不必加上class关键字。。
    2. C和C++都支持同结构类型的结构变量使用 = 赋值。。。成员值会完全相同。。
    3. 结构体可以声明构造函数,可以声明自己的方法,可以重载操作符,可以有public和private等权限。类的默认权限是private,struct的默认权限是public。稍后总结。。

    第十条:逻辑操作符的其它表示方法

    C++为逻辑操作符提供标识符表示方法:and,or,not。这些都是C++的保留字,可以直接使用。但是它们在C中不是保留字。只有在程序中#include才能使用它们。
    
    逻辑操作符的另一种表示方式
    操作符 替换代表
    && and
    || or
    ! not

    第十一条:函数

       C语言和C++在函数上大体是相同的。C++支持绝大部分C语言的函数语法。而C++增添了很多特有的函数功能,如内联函数,引用变量,默认参数,函数重载和函数模板。函数由函数头和函数体组成。
    1. 函数原型:函数原型就是函数头后加上;。C++中在在第一次使用函数前,必须先用函数原型声明函数,或者直接将函数定义放在前面。否则,编译器会报错,函数未声明.
      在C中对此没有显性要求。可以不声明函数原型。声明函数原型时括号可以为空,这只是表示函数的参数先不告诉编译器,但是参数是确定的。而C++中为空则表示参数为void。同时与fun(...)也不一样。‘...’表示参数不确定。三者要分清楚。当然,从编程风格来讲,C语言中最好也要声明函数原型。
    2. 内联函数与宏定义:内联函数就是编译器在编译时,会直接把函数的指令插入到内联函数调用处。而不是采取堆栈式管理方式,跳转到函数入口处。每一处函数调用都会插入函数指令,但是省去了跳转时间。“空间换时间”。如果函数的执行时间很长,那么跳转时间占比例很小,“不值得”。如果函数的执行时间较短,跳转时间占比例大。但是绝对时间又不大。所以,内联函数适合短小但是经常被调用的函数。因此多应用在类设计中
      同时,编译器也会对内联函数作出判断,如果编译器自己认为函数不宜被设计为内联函数,如函数过大,或者函数含有递归。那么编译器也不会将其编译为内联的。
      内联函数格式:inline 函数原型/函数定义
      C语言中含有参数的宏定义#define FUN(X) XXX 有类似功能。但是两者有本质差别。预处理器#define实现的是编译前的文本替换。将源代码中的文本替换之后,进行编译。而内联函数,是编译时将函数指令替换函数调用。具体差别可以查看C语言关于#define宏定义。
      C语言中自从C99之后,也支持inline函数了。
    3. 引用:引用与const指针相似,必须在声明引用时进行初始化,const变量也是。而const指针和引用都是一旦与某个变量关联,就保持这种联系,不再改变。
      const int * ptr是指向const的指针,int * const ptr是const指针。
      因为引用参数的不确定性,所以不建议对简单的基本类型变量使用引用参数。当参数的数据比较大,如数据结构和类时,使用引用参数将节省空间和时间。但是除非明确要使用引用参数达到修改变量的目的,否则在函数定义时,要使用const Type & argument..
      传递引用的限制更严格,如果实参与引用参数不匹配,如,实参为非左值(字面变量,包含多项的表达式)或者类型不匹配时,当形参引用参数为const时,将生成临时变量,非const时,绝大部分编译器会报错。少数会生成临时变量。所以应尽可能将引用形参声明为const。
    4. 默认参数:一:默认参数可以只在函数原型中声明,二,默认参数必须从右到左添加默认值,默认参数的右边也必须是默认参数。否则,fun(int i = 0, int j),那么调用fun(3)。3是覆盖i还是赋值给j,无法判断。
      1. 函数重载:函数重载的关键在于函数的参数列表,即特征标(function signature)不同。
          现讨论六种比较有疑惑的函数;按值传递:int fun(int a), int fun(const int a),引用形参: int fun(int & a), int fun(const int & a),指针形参 int fun(int * a), int fun(const int *a)。指针形参和其它两组显然可以重载。
        • 对于按值传递int fun(int a)和int fun(const int a)无法重载。因为显然编译器在调用fun(a)时,无法区别选择哪一个。
        • 按值传递和引用形参无法进行重载。
          int fun(int & a)和int fun(int a)以及int fun(const int a)是同样的。调用fun(a)时,编译器无法区别调用的是哪个函数。因此会报错,重复定义函数。
        • 引用形参的const和非const形式,指针形参的const和非const形式均可以视为重载。因为const实参只能赋值给const形参,所以编译器对const实参调用const形参,对非const实参调用非const形参。
        • 重载是对参数列表进行重载,而不是返回类型。所以参数列表相同,返回类型不同不算重载,编译报错。参数列表不同,则返回类型也可以不同。

          当实参与形参的类型不匹配时,会按照一定的规则进行自动类型转化。面对重载的函数,如果按照自动类型转换规则,只有一个函数适合,那么编译器就调用那个函数。如果有多个函数适合,编译器报错,有二异性。

        void fun(double x);                                    *          void fun(double x);            
        void fun(int x);                                       *          void fun(float x);
        {                                                     *          {
            float x = 9.0;                                     *               int x = 9; 
            fun(x);                                            *               fun(x);
        }                                                      *          }
        float转换为double优于转换为int,所以编译器会调用fun(double)            int转换为float和double均可以,所以编译器报错,调用fun会产生二异性。
    5. 函数模板:适用:需要多个将同一种算法用于不同类型的函数。函数模板并不是减少了生成函数的个数,最终编译出来的函数个数和指令还是不变的。它只是为函数的定义在编写上提供遍历。
      函数模板包括三方面:函数模板的隐式实例化(implicit instantiation),后期的C++也支持显式实例化(explicit instantiation),显式具体化(explicit specialization)。格式如下
      template <class T> void fun(T a); //模板原型,typename和class两个关键字可以互换
      template void<int> fun(int a);//显式实例化的原型


      template<> void fun (int a); //
      template <> void fun(int a);//上面和这个两者都是显式具体化的原型,是等价的声明
      模板原型和模板定义与函数原型和函数定义一样。在使用模板前必须声明模板原型。模板也可以重载,同名模板的参数列表不同,这与函数重载一样。由此,对于一个函数名,可以有非模板函数,模板函数,和显式具体化以及它们的重载提供的各种版本。调用函数时它们的优先级大致为非模板函数>显式具体化>显式实例化>模板函数。对于非模板函数重载的版本,根据形参和实参最佳匹配原则确定调用版本。对于模板重载函数,根据部分排序规则确定调用版本,就是找出最具体的模板。如果能找出一个最佳函数,则编译成功。否则,编译会报出二异性错误,函数无法解析。函数的选择是个非常细致和复杂的过程,可以找具体资料查询。
  • 相关阅读:
    yield return 和 Func
    匿名类型与扩展方法
    对象初始化器和集合初始化器
    VSCode编辑器使用技巧:快捷输入HTML代码
    CSS清除浮动
    置换元素与非置换元素
    浏览器五大内核及代表
    IE过滤器
    写个 Hello world 前端从入坑到弃坑系列教程(1)
    测试一下
  • 原文地址:https://www.cnblogs.com/xyqhello/p/3517833.html
Copyright © 2011-2022 走看看