• 学习C++要注意的那点事


    C与C++头文件的区别和联系

    1、旧的 C++ 头文件,如 iostream.h、fstream.h 等将会继续被支持,尽管它们不在官方标准中,这些头文件的内容不在命名空间 std 中。新的 C++ 头文件,如 iostream、fstream 等包含的基本功能和对应的旧版头文件相似,但头文件的内容在命名空间 std 中。

    2、标准C头文件如 stdio.h、stdlib.h 等继续被支持,头文件的内容不在 std 中。具有C库功能的新C++头文件具有如 cstdio、cstdlib 这样的名字,它们提供的内容和相应的旧的C头文件相同,只是内容在 std 中。

    命名空间声明使用的注意事项

    #include <iostream>
    using namespace std;  //声明命名空间std
    int main(){
        cout<<"......."<<endl;
    return 0;
    }
    

       将std直接声明在所有函数外部,这样虽然使用方便,但在中大型项目开发中是不被推荐的(小型项目或者测试学习声明在全局变量中还是很方便的),这样做增加了命名冲突的风险,推荐在函数内部声明std

    #include <iostream>
    void func(){
        using namespace std;  //必须重新声明
        cout<<"http://c.biancheng.net"<<endl;
    }
    int main(){
        using namespace std;  //声明命名空间std
        cout<<"。。。。。。"<<endl;
    func();
    return 0;
    }
    

    Newdelete

      和 malloc() 一样,new 也是在堆区分配内存,必须手动释放,否则只能等到程序运行结束由操作系统回收。为了避免内存泄露,通常 new deletenew[] delete[] 操作符应该成对出现,并且不要和C语言中 malloc()free() 一起混用。

    例如:

    int *p = new int;  //分配1个int型的内存空间
    delete p;  //释放内存
    ----------------------------------------或----------------------------------------
    int *p = new int[10];  //分配10个int型的内存空间
    delete[] p;
    

    函数的默认参数

    #include<iostream>
    using namespace std;
    //带默认参数的函数
    void func(int n, float b=1.2, char c='@'){
        cout<<n<<", "<<b<<", "<<c<<endl;
    }
    int main(){
        //为所有参数传值
        func(10, 3.5, '#');
        //为n、b传值,相当于调用func(20, 9.8, '@')
        func(20, 9.8);
        //只为n传值,相当于调用func(30, 1.2, '@')
        func(30);
        return 0;
    }
    

      C++规定,默认参数只能放在形参列表的最后,而且一旦为某个形参指定了默认值,那么它后面的所有形参都必须有默认值。实参和形参的传值是从左到右依次匹配的,默认参数的连续性是保证正确传参的前提。

    函数重载

    函数的重载的规则:

    1. 函数名称必须相同。
    2. 参数列表必须不同(个数不同、类型不同、参数排列顺序不同等)。
    3. 函数的返回类型可以相同也可以不相同。
    4. 仅仅返回类型不同不足以成为函数的重载。
    //交换 int 变量的值
    void Swap(int *a, int *b){
        int temp = *a;
        *a = *b;
        *b = temp;
    }
    //交换 float 变量的值
    void Swap(float *a, float *b){
        float temp = *a;
        *a = *b;
        *b = temp;
    }
    //交换 char 变量的值
    void Swap(char *a, char *b){
        char temp = *a;
        *a = *b;
        *b = temp;
    }
    //交换 bool 变量的值
    void Swap(bool *a, bool *b){
        char temp = *a;
        *a = *b;
        *b = temp;
    }
    

     C++是如何做到函数重载的

      C++代码在编译时会根据参数列表对函数进行重命名,例如void Swap(int a, int b)会被重命名为_Swap_int_intvoid Swap(float x, float y)会被重命名为_Swap_float_float。当发生函数调用时,编译器会根据传入的实参去逐个匹配,以选择对应的函数,如果匹配失败,编译器就会报错,这叫做重载决议(Overload Resolution)。不同的编译器有不同的重命名方式,这里仅仅举例说明,实际情况可能并非如此。从这个角度讲,函数重载仅仅是语法层面的,本质上它们还是不同的函数,占用不同的内存,入口地址也不一样。

    函数模板

    值(Value)和类型(Type)是数据的两个主要特征,它们在C++中都可以被参数化。

      所谓函数模板,实际上是建立一个通用函数,它所用到的数据的类型(包括返回值类型、形参类型、局部变量类型)可以不具体指定,而是用一个虚拟的类型来代替(实际上是用一个标识符来占位),等发生函数调用时再根据传入的实参来逆推出真正的类型。这个通用函数就称为函数模板(Function Template)。

    #include <iostream>
    using namespace std;
    template<typename T> void Swap(T *a, T *b){
        T temp = *a;
        *a = *b;
        *b = temp;
    }
    int main(){
        //交换 int 变量的值
        int n1 = 100, n2 = 200;
        Swap(&n1, &n2);
        cout<<n1<<", "<<n2<<endl;
        //交换 float 变量的值
        float f1 = 12.5, f2 = 56.93;
        Swap(&f1, &f2);
        cout<<f1<<", "<<f2<<endl;
        return 0;
    }
    

       template是定义函数模板的关键字,它后面紧跟尖括号<>,尖括号包围的是类型参数(也可以说是虚拟的类型,或者说是类型占位符)。typename是另外一个关键字,用来声明具体的类型参数,这里的类型参数就是T。从整体上看,template<typename T>被称为模板头。

    定义模板函数的语法:

    template <typename 类型参数1 , typename 类型参数2 , ...> 返回值类型  函数名(形参列表){
        //在函数体中可以使用类型参数
    }
    

    typename关键字也可以使用class关键字替代,它们没有任何区别。类型参数不能为空,多个类型参数用逗号隔开。

    类模板

    声明类模板的语法为:

    template<typename 类型参数1 , typename 类型参数2 , …> class 类名{
        //TODO:
    };
    

    模板头和类头是一个整体,可以换行,但是中间不能有分号。例如:

    template<typename 类型参数1 , typename 类型参数2 , …>  //这里不能有分号
    class 类名{
        //TODO:
    };
    

    例子:

    template<typename T1, typename T2>  //这里不能有分号
    class Point{
    public:
        Point(T1 x, T2 y): m_x(x), m_y(y){ }
    public:
        T1 getX() const;  //获取x坐标
        void setX(T1 x);  //设置x坐标
        T2 getY() const;  //获取y坐标
        void setY(T2 y);  //设置y坐标
    private:
        T1 m_x;  //x坐标
        T2 m_y;  //y坐标
    };
    

    上面的代码仅仅是类的声明,我们还需要在类外定义成员函数。在类外定义成员函数时仍然需要带上模板头,格式为:

    template<typename 类型参数1 , typename 类型参数2 , …>
    返回值类型 类名<类型参数1 , 类型参数2, ...>::函数名(形参列表){
        //TODO:
    }
    

    第一行是模板头,第二行是函数头,它们可以合并到一行,不过为了让代码格式更加清晰,一般是将它们分成两行。

    template<typename T1, typename T2>  //模板头
    T1 Point<T1, T2>::getX() const /*函数头*/ {
        return m_x;
    }
    template<typename T1, typename T2>
    void Point<T1, T2>::setX(T1 x){
        m_x = x;
    }
    

      除了 template 关键字后面要指明类型参数,类名 Point 后面也要带上类型参数,只是不加 typename 关键字了。另外需要注意的是,在类外定义成员函数时,template 后面的类型参数要和类声明时的一致。

    强制类型转换

    强制类型转换的格式为:

    (type_name) expression
    

     type_name为新类型名称,expression为表达式。例如:

    (float) a;  //将变量 a 转换为 float 类型
    (int)(x+y);  //把表达式 x+y 的结果转换为 int 整型
    (float) 100;  //将数值 100(默认为int类型)转换为 float 类型
    

      可以自动进行的类型转换一般风险较低,不会对程序带来严重的后果,例如,int double 没有什么缺点,float int 顶多是数值失真。只能强制进行的类型转换一般风险较高,或者行为匪夷所思,例如,char * int * 就是很奇怪的一种转换,这会导致取得的值也很奇怪,再如,int char * 就是风险极高的一种转换,一般会导致程序崩溃。
    使用强制类型转换时,程序员自己要意识到潜在的风险。

     

    预处理实现识别WindowsLinux

    不同的平台下执行不同的代码段

    #if _WIN32  //识别windows平台
    ......
    #elif __linux__  //识别linux平台
    ......
    #endif
    

    多文件程序的编写

    如何防止多次包含同一个*.h的头文件

    使用#ifndef技巧,通用模板如下:

    /* things.h */
    #ifndef THINGS_H
    	#define THINGS_H
    	/* 省略头文件的其他内容 */
    #endif
    

    头文件标识符宏定义(习惯上的)命名规则:用文件名作为标识符、使用大写字母、用下划线字符做前缀或后缀

  • 相关阅读:
    网页轮播图案例
    表单
    表格标签的使用
    HTML5标签2
    HTML标签
    外边距
    h5css产品模块设计
    mouseenter 和mouseover的区别
    动画函数封装
    jQuery 插件
  • 原文地址:https://www.cnblogs.com/haijian/p/15912024.html
Copyright © 2020-2023  润新知