初识CPlusPlus(序)

前言

        本文是紧接着上一篇《初识CPlusPlus》的后续内容,当然也不是最后,只是现阶段整理出来的就这么多。写的也回更加详细点,讲出作用意义。

        本文,将介绍C++中的引用、函数模版、inline函数、类和对象及其中具体内容,包括构造函数、析构函数等6大函数,拓展的2种函数留到之后再讲。

一、引用

1、简介

        引用就是祖师爷觉得C语言中的指针太麻烦了,每次使用都需要解引用“*”。所以直接让计算机帮你完成了解引用的操作,这就是引用的本质。

        实际上使用引用就像是取别名一样,像《水浒传》中的豹子头林冲那样,取得别名。在日常生活中就像小名一样,反正就是叫的同一个人。

2、举例说明

2.1、引用的使用方式

        我们可以定义一个整形变量a,然后把它重命名为c,b,也可以把这里的b引用给d。这样上述4个变量其实都是同一个变量:

int main()
{
    int a = 10;
    int& b = a;
    int& c = a;
    int& d = b;
    cout << "a = " << a << " &a = " << &a << endl;
    cout << "b = " << b << " &b = " << &b << endl;
    cout << "c = " << c << " &c = " << &c << endl;
    cout << "d = " << d << " &d = " << &d << endl;
    return 0;
}

2.2、引用和指针的区别

        这里的区别就简单提一嘴,引用虽然是指针,但是引用不能像指针那样改变指向。因此引用必须在最开始的时候就给出引用的对象。以下写法没有给出对象是错误的:

int& b;

        和指针的相同点,就是存在着权限的问题。引用的时候不能放大权限,在指针的时候我们知道给const变量定义指针的时候需要用const修饰指针指向的变量,例如:

int main()
{
    const int a = 10;
    const int* b = &a;
    
    return 0;
}

        对于引用来说也是相同的,例如:

int main()
{
    const int a = 10;
    const int* b = &a;

    const int& c = a;
    const int& d = 10;
    
    return 0;
}

        常量也是可以引用的,但是不能修改,需要const修饰。

2.3、函数与引用

        函数中也可以使用引用,它的效果和指针类似。用法也类似,需要注意的点也大差不差。实际上是指针,所以函数参数和返回值都能够使用引用进行传递。但也要注意不要出现以下状况:如果传递回去的返回值是需要在函数中销毁的(将亡值),那么就不能使用引用返回。

int& func()
{
    int a = 0
    return a;
}

        因为出了函数作用域,函数内定义的局部变量会销毁,原来存值的空间可能会被其他程序使用,那么返回值会出错。除此之外还要注意引用传值的修改会影响所有值,和指针相同。因为指向的位置是同一个空间。

int main()
{
    int a = 10;
    int& b = a;
    int& c = a;
    int& d = b;
    d = 20;
    cout << "a = " << a << " &a = " << &a << endl;
    cout << "b = " << b << " &b = " << &b << endl;
    cout << "c = " << c << " &c = " << &c << endl;
    cout << "d = " << d << " &d = " << &d << endl;
    return 0;
}

        所以函数中需要注意,虽然引用和指针一样会减少空间的使用,但也要注意,如果使用过程中不需要修改值的话,最好加cosnt修饰,以免程序出错。

二、函数模版

        函数模板定义的一般形式如下:

template<类型形式参数表>

返回类型 函数名(形式参数表)

{

... //函数体

}

        它的意义在于减少了重复劳动,交给计算机编写函数,例如之前写的函数:

int Add(int x, int y)
{
    return x + y;
}

double Add(double x, double y)
{
    return x + y;
}

        现在只需要利用函数模版就能解决:

template <class T>
T Add(T a, T b)
{
    return a + b;
}

        这样的情况下不仅包含了浮点数、整数,其他的类型也会按照模版自动推算。另一方面,模版也可以具体化,那么函数会优先调用最接近的。这个举例之后重新学到的时候会仔细讲,这里只是浅提一嘴。

三、inline修饰的函数

1、简介

        宏函数,想必大家都清楚。优点是不需要想函数那样栈针,而是编译的时候直接替换。缺点则是写起来需要加括号明确优先级、并且不好调试。为了解决这样的问题,就有了inline函数。效果是能够让函数不栈针,直接在对应位置展开。在汇编里面就相当于不会看到call。

        需要注意的是,inline修饰的函数只是向编译器建议直接展开。如果函数太长,或者其他的原因可能在编译过后还是向普通函数那样执行。

2、特点

        inline修饰的函数也被称为内联函数,内联函数不会展开,不建立栈帧:

        (1)inline对于计算机来说是建议不建立栈帧,是建议。

        (2)宏函数也不需要展开,这里出现inline就是为了替代宏函数。

        (3)debug版本下默认不展开inline方便调试。

        (4)内联函数不建议声明和定义分离到两个文件,分离可能会导致链接错误,因为inline不被展开,就不会去找函数的地址,链接可能出错。

3、使用举例

        函数前面增加inline即可。

inline void func(int a, int b){}

四、类和对象

1、简介

        类和对象是C++出现的,如果说C语言是面向过程的,那么因为类和对象,C++变成了面向对象的语言。

        类的使用和结构体类似,不过在C++中的类和结构体能够包括函数,也就是说能够更方便的找到对应结构体的操作函数。除此之外还优化了C语言中,结构体的名字需要增加“struct”的问题。在C++中,这个关键字只需要出现在建立结构体的时候就可以了。

        类的关键字是“class”。

2、访问限定字符

        为了改进C语言中容易随意修改结构体成员,C++中引入了3整访问限定符:protect、private、public。

        protect:保护,能够保护成员、该成员无法直接调用、可以被继承。

        private:私有,能够保护成员、该成员无法直接调用且该成员无法被继承。

        public:公有,能够被随意调用,可以被继承。

        在类中如果不写这些关键字,那么默认是私有。结构体中默认是公有。

        使用方法,关键字加“:”从此之后都表示是同一类型,直到遇到另外的关键字,例如:

class Exp
{
public:
    void func()
    {
        cout << "func()" << endl;
    }

private:
    int a;
    int b;

public:
    double d;
};

        在这个类中函数“func()”和“d”都是公有的,而变量“a”和“b”是私有的。

        那么接下来来介绍访问的区别:

int main()
{
    Exp exp; // 也可以写作class Exp
    // exp.a;
    // exp.b;
    // 对于 a 和 b 因为是私有无法直接访问,只能通过类里面的公共函数,或者友缘函数访问
    exp.d = 3.33;
    exp.func();
    // 而变量 d 和 函数 func()是可以直接访问,变量d也可以修改
    return 0;
}

3、类的大小

        类的大小和结构体大小的计算方式相同,只需要看变量的大小。类里面的函数不存在于类中,而是在静态区。

4、this指针

        this指针就相当于指向这个结构体的指针,可以直接在结构体中使用。例如在用函数去成员变量的时候:

class Exp2
{
public:
    void func()
    {
        _a = 1;
        _b = 2;
        cout << _a << endl;
    }

private:
    int _a;
    int _b;
};

        如上结构体所示,函数中取了该类中的变量,那么这个函数是怎么找到这些变量的呢?实际上是计算机会自动添加“this->”帮你取到该类的成员,这样才不会取到其他地方。如果手动添加了那么计算机就不会继续添加。

        该类的函数实际的样貌是:

void func()
{
    this->_a = 1;
    this->_b = 2;
    cout << this->_a << endl;
}

5、类的默认访问成员函数

        在C++98的时候有6种编译器会默认生成的成员函数。例如:构造函数、析构函数、拷贝构造、赋值重载等等。

        接下来就简单介绍一下这4种函数:

5.1、构造函数

        构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数类型的不同来区分它们 即构造函数的重载

        特点:

        (1)构造函数名字于类的名字相同

        (2)无返回值,void也不行

        (3)对实例化对象自动调用

        (4)不手动写计算机就会自动生成,但是如果需要申请空间,就需要自己写

        (5)构造函数能够有重载

        (6)默认构造函数只有一个,就是没有形参的那个。

        (7)默认生成的构造函数对初始化没有要求,所以构造函数中如果有自定义类型或者指针建议自行写。

        使用举例:

class Exp3
{
public:
    Exp3(int a, int b)
        :_a(a) // 这种赋值方法是c++中增加的在变量后加括号,表示调用它的构造
        ,_b(b) // 所以通过这种方式也能够初始化内置类型
    {}

    Exp3() // 构成重载
    {
        _a = 1;
        _b = 2;
    }

private:
    int _a;
    int _b;
};

        默认构造函数的调用是在如下情况,没有赋值的直接生成:

int main()
{
    Exp3 exp;
    
    return 0;
}

        也可以通过构造函数直接对成员变量进行赋值,使用方法如下:

int main()
{
    Exp3 exp(3, 4);

    return 0;
}

5.2、析构函数

        与构造函数相反,当对象结束其生命周期,如对象所在的函数已调用完毕时,系统会自动执行析构函数。以C++语言为例:析构函数名也应与类名相同,只是在函数名前面加一个位取反符~,例如~stud( ),以区别于构造函数。它不能带任何参数,也没有返回值(包括void类型)。只能有一个析构函数,不能重载。如果用户没有编写析构函数,编译系统会自动生成一个缺省的析构函数(即使自定义了析构函数,编译器也总是会为我们合成一个析构函数,并且如果自定义了析构函数,编译器在执行时会先调用自定义的析构函数再调用合成的析构函数),它也不进行任何操作。所以许多简单的类中没有用显式的析构函数。

        特点:

        (1)析构函数在类名前增加~

        (2)无参数、无返回值

        (3)一个类只有一个析构函数,没有会自动生成

        (4)生命周期结束会自动调用,就是出了函数就析构

        (5)析构时,自定义的类型会调用自己的析构函数,且这个过程会有计算机帮助完成,故不需要写,内置类型不做处理。

        (6)自定义的类型会调用自己的析构函数

        (7)析构的顺序和栈类似,后定义的先析构。

        使用举例:

class A
{
public:
    ~A()
    {
        cout << "~A()" << endl;
    }
};

class B
{
public:
    ~B()
    {
        cout << "~B()" << endl;
    }
};


class C
{
public:
    ~C()
    {
        cout << "~C()" << endl;
    }

private:
    int _c;
    A _a;
    B _b;
};

void func()
{
    C c;
}

int main()
{
    A a;
    func();
    B b;
    return 0;
}

        在以上例子中,生成了a、b、c三个变量,其中c先到生命周期——到创建它的函数的结尾,所以会先析构。然后到main()函数结束,会先析构后创建的变量b,最后析构a。

5.3、拷贝构造

        每一个类只有一个析构函数,但可以有多个构造函数(包含一个默认构造函数,一个拷贝构造函数,和其他普通构造函数)和多个赋值函数(包含一个拷贝赋值函数,其他的为普通赋值函数)。

        特点:

        (1)拷贝构造名字于类的名字相同

        (2)无返回值,void也不行

        (3)对实例化对象自动调用

        (4)不手动写计算机就会自动生成,但是如果需要申请空间,就需要自己写

        (5)拷贝构造有且只有一个形参,而且形参需要增加&符号。

        使用举例:

class Date
{
public:
    // 构造函数
    Date(int year = 0, int month = 0, int day = 0)
        :_year(year)
        ,_month(month)
        ,_day(day)
    {}

    // 拷贝构造
    Date(const Date& date)
    {
        _year = date._year;
        _month = date._month;
        _day = date._day;
    }

    // 内置类型,析构可以机器自动生成

    void Print()
    {
        cout << _year << "年" << _month << "月" << _day << "日" << endl;
    }

private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    Date d1(2024, 7, 11);
    Date d2(d1);    // 拷贝构造的第一种用法
    Date d3(1921, 7, 23);

    d1.Print();
    d2.Print();

    cout << endl;
    d1 = d3; // 拷贝构造的第二种用法
    d1.Print();

    return 0;
}

5.4、赋值重载

        操作符重载,计算机学科概念,就是把已经定义的、有一定功能的操作符进行重新定义,来完成更为细致具体的运算等功能。操作符重载可以将概括性的抽象操作符具体化,便于外部调用而无需知晓内部具体运算过程。

        例如我们使用流插入、流提取的时候,用的就是操作符重载。将<<重载为输出,将>>重载为输入。原来在C语言中表示移位的操作符就有了另外的作用。

        用法是operator加上重载的符号。

        使用举例:

        例如我们需要比较日期的大小,那么年月日可能都要比较到,所以需要用到赋值重载。

class Date
{
public:
    // 构造函数
    Date(int year = 0, int month = 0, int day = 0)
        :_year(year)
        ,_month(month)
        ,_day(day)
    {}

    // 拷贝构造
    Date(const Date& date)
    {
        _year = date._year;
        _month = date._month;
        _day = date._day;
    }

    // 内置类型,析构可以机器自动生成

    void Print()
    {
        cout << _year << "年" << _month << "月" << _day << "日" << endl;
    }

    bool operator>(const Date& date)
    {
        if(_year > date._year)
        {
            return true;
        }
        else if(_year == date._year)
        {
            if(_month > date._month)
            {
                return true;
            }
            else if(_month == date._month)
            {
                if(_day > date._day)
                {
                    return true;
                }
            }
        }
        return false;
    }

    bool operator<=(const Date& date)
    {
        return !(operator>(date));
    }

private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    Date d1(2024, 7, 11);
    Date d2(d1);    
    Date d3(1921, 7, 23);

    cout << (d1 > d3) << endl;
    cout << (d1 <= d2) << endl;


    return 0;
}

        另外+、-、*、/等符号都可用赋值重载进行操作符重载。

        这一类重载的函数反而最多,剩下的交给读者自行探索。

作者结语

        这一次内容还挺多的,整理了一下也还是写了好多,其中的引用、函数模版、类的用处很多很广,学到这里我们也就刚刚入门了。大佬的代码估计还是看不太懂。毕竟C++有太多的封装嵌套,然后还有各种离谱操作。

        学吧,啥时候整到无敌的水平,就能够靠编程在这个世界横着走。我愿称此为编程界,愿大家早日得道成尊。

        ps:“没有乐土大人,我们如何抗衡双尊”,方源哈哈大笑“这简单,我成尊不就是了?”

  • 23
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值