Cpp day02

一、什么是默认形参

​ 在C++中可以给函数的参数设置默认值,当函数被调用时如果调用者提供了实参则使用实参,如果没提供则使用默认值。

1、默认形参要靠右

​ 如果函数有多个参数,设置了默认形参要从右到左连续排列。

2、只在声明设置设置默认形参

​ 如果函数的声明和定义分开实现,只在函数声明时设置默认形参即,否则会出现语法错误。

3、默认形参可能会影响重载的效果

​ 如果对函数进行了重载双设置的默认形参,调用时可能会造成冲突。
​ 因此为重载过的函数设置默认形参时一定要慎重。

二、内联函数

1、普通函数

​ 普通函数会被翻译成二进制指令存储在代码段中,调用语句会生成一条跳转指令,当程序运行时到调用语句时,会跳转到函数所在的代 码位置执行,执行结束后再返回。

2、什么是内联函数

​ 内联函数也会被翻译成二进制指令,调用语句不会生成跳转语句,而直接把函数的二进制指令直接替换调用语句,这样就没有跳转也没 有返回,而是直接执行被调函数,我们把这种函数叫内联函数。

3、隐式内联和显式内联

​ 显示内联:在函数的返回值前面添加 inline 该函数就会以内联机制调用,不是所有的编译器都支持,我们现在使用的g++和gcc就不支 持。

​ 隐式内存:结构、联合、类的成员函数会自动被当作内联函数处理。

4、内联适用的条件

​ 内联的优点:
​ 节约了函数传参、跳转、返回的时间,提高代码运行速度。
​ 内联的缺点:
​ 当内联函数在多个位置被调用,那么该的二进制指令就被拷贝了多份,就样就产生了冗余,因此代码增加导致可执行文件增大。
​ 1、内容简单且会频繁调用的函数(一次调用会被执行到多次),内容多且极当被调用到的函数不适合内联,因为它节约的时间弥补不了牺 牲的内存。
​ 2、带有递归特性的函数无法内联,编译器会自动忽略inline关键字。

5、内联函数与宏函数的相同点和不同

​ 相同点:
​ 都是采用空间换取时间的策略来提高程序的运行速度。
​ 不同点:
​ 1、宏函数不是真正的函数,而是语句替换,不会对参数进行类型检查,没有返回值,安全性低。
​ 2、内联函数是真正的函数,会严格检查参数,有返回值,比宏函数的安全性高。

三、引用

​ 什么是引用:引用是一种取别名的机制。

​ 为什么使用指针:
​ 1、跨函数共享变量(输出型参数),引用可替代。
​ 2、提高传参效率,引用可替代。
​ 3、配合字符串,string可替代。
​ 4、配合堆内存,继续使用指针。

什么情况下使用引用

​ 1、跨函数共享变量,比指针安全(不存在空引用,也极少出现野引用),比指针方便(不要取地址、解引用)。
​ 2、提高传参效率,引用的传参效率比指针还高,因为指针还需要4/8字节用于存储地址编号,而引用一个字节都不需要,但引用和指 针一样都有被修改的风险,因此为了保护目标要增加const。

使用引用要注意的问题

​ 1、引用必须初始化,所以不存在空的引用。
​ 2、可以引用右值,但必须使用cosnt修改。
​ 3、引用不可以更换目标
​ 4、返回引用型数据时,不要返回局变量的引用。

指针与引用的相同点和不同点

​ 相同点:
​ 都可以跨函数共享内存,都可以提高函数的传参效率,也都需要const保护。

​ 不同点:
​ 1、引用是一种取别名机制,而指针是一种数据类型。
​ 2、引用不需要额外的存储空间,而指针需要4/8字节用于存储内存地址。
​ 3、指针可以不初始化,引用必须初始化。
​ 4、有空指针,但没有空引用。
​ 5、指针可以更换指向目标,而引用不可以。
​ 6、指针可以配合堆内存使用,而引用不可以。
​ 7、可以定义指针数组,但不能定义引用数组(可以定义数组指针,也可以定义数组引用,可以定义函数指针,也只可以函数引 用)。

四、强制类型转换

​ 由于C语言中的强制类型转换还能继续使用,所以C++中的强制类型转换就是很鸡肋。

​ 为什么C++重新设计的强制类型转换?

​ 因为C语言中的强制类型转换太危险。

​ 为什么C++的强制类型转换设计的很复杂,使用很麻烦?

​ 因为C++之父认为只有在程序设计的不合理的情况下,才需要强制类型转换,之所以设计这么复杂是不想让程序员使用,从而去反 思、重新设计自己的代码。

1、静态类型转换

​ static_cast<目标类型>(原类型)
​ 目标类型和源类型之间必须一个方向可以自动类型转换,否则就会出现 错误。

2、动态类型转换

​ dynamic_cast<目标类型>(原类型)
​ 源类型和目标类型之间必须存在继承关系,否则会出现错误。

3、去常类型转换

​ const_cast<目标类型>(原类型)
​ 源类型和目标类型必须是指针或引用,且间除了const属性外,其它都相同,否则就会出现错误。

4、reinterpret_cast

​ reinterpret_cast<目标类型>(原类型)
​ 只能把整数转换成指针或把指针转换成整数,否则就会出现错误。

五、面向对象与面向过程

​ 面向过程:
​ 关注的是如何解决问题,以及解决问题的步骤。

​ 面向对象:
​ 抽象:先找出(想出)能解决的"对象",分析该对象解决所需要的属性(成员变量)和行为(成员函数)。
​ 封装:把抽象的结构封装成一个类(结构),并给类的成员函数、成员函数设置相应的访问权限(public、protected、private)。
​ 继承:
​ 1、在封装类先考虑现有的类是否能解决一部分问题,如果有则把现在的继承过程,在此基础上进行扩展,来缩短解决问题的时间。
​ 2、把一个复杂的问题拆分成若干个小问题,每个问题设计一个类去解决,最后把这些类通过继承合并成一个能解决复杂问题的类。
​ 多态:
​ 发出一个指令,系统会根据实际情况执行相就操作,这种特性就叫多态(同一个命令有多形态)。
​ 比如重载过的函数,当调用函数,编译器会根据参数的类型,调用相应的重载版,这就是一种多态。
​ 具体调用哪个版的函数是编译器在编译时就能确定,因此调用重载函数也叫编译时多态。

​ 注意:但是行为的细节依然是面向过程,面向对象是以更高的维度去思考问题,而不是寻找解决问题的捷径。

六、类和对象

​ 什么是类和对象

​ 类是由程序员设计一种数据类型,它里面包含成员变量、成员函数。
​ 而对象是类的实例化,可以里面为使用类创建的变量。

类的设计和实例化
    class 类名
    {
        成员变量;   // 类中的默认属性是private
    public:
        成员函数;
    };

    方法1:类名 类对象;
    方法2:类名* p = new 类名;

类型声明、实现和使用
在头文件中声明:
    class 类名
    {
        成员变量;
    public:
        返回值 函数名(参数列表);
    };

在源文件中实现:
    返回值 类名::函数名(参数列表)
    {
        // 在成员函数中可以直接使用成员变量、调用其它成员函数,不需要 . ->
    }
注意:如果类的内存不多,也可以考虑在头文件中全部实现。

七、访问控制限定符

private

​ 私有的,被它修饰的成员只能在类内访问,这也是类的默认访问属性。
​ 设置为私有是为了对类进行保护,一般只有成员变量设置为私有的。

public

​ 公开的,被它修改的成员可以在任何位置访问,一般把类的成员函数设置公开的。

protected

​ 保护的,被它修饰的成员可以在类内和子类中访问,但不能在类外访问。

八、构造函数

​ 构造函数是类的同名成员函数,当创建类时它会自动执行,一般负责对类进行初始化、分配资源。

    class 类名
    {
        int* p;
    public:
        类名(参数) // 可重载
        {
            p = new int;
        }
    }

1、构造函数必须是 public 否则无法创建对象
2、构造函数可以重载,可以有多个版本
3、带参数的构造函数的调用

    Test t(实参);
    Test* p = new Test(实参);

4、默认情况下编译器会自动生成一个没有参数,什么都不做的构造函数,一旦显式实现带参的构造,无参构造不会再生成。

    Test t; // 调用无参构造,如果没有无参构造,该语句会出错

5、也可以通过设置默认形参达无参构造的效果
6、构造函数没有返回值
7、不要使用malloc为类对象分配内存,因为不会调用析构函数。

九、析构函数

​ 析构函数负责对类对象进行收尾,如:释放类中的资源,保存数据等,当类对象销毁时会自动执行。

    class 类名
    {
        int* p;
    public:
        类名()
        {
            int* p = new int;
        }
        ~类名()
        {
            delete p;
        }
    };

1、析构函数也必须是public
2、析构函数没有参数,没有返回值,不能重载。
3、当类对象的生命周期到期时,可使用delete销毁对象时会自动调用析构函数。
4、构造函数肯定会执行,但析构函数不一定会执行。
5、不要使用free销毁类对象,这样就不会调用析构函数。
6、如果没有显式实现析构函数,编译器会自动生成一个什么都不做的析构函数。

十、初始化列表

​ 初始化列表是构造函数的一种特殊语法,只能在构造函数中使用。

    class 类名
    {
    public:
        类名():成员1(初始化数据),成员2(初始化数据),...
        {

        }
    };

1、类的成员变量不可以设置初始值,而在构造函数执行时类成员变量已经定义完毕,因为const成员变量无法在构造函数中初始化。
2、初始化列表先于构造执行,初始化列表执行时类对象还没构造完成,因此它是唯一一种给const成员赋值的方法。
3、当参数名与成员名相同时,它可以自动分辨成员和参数。
4、如果有成员是类这种类型,哪些成员的有参构造也可以在初始化列表中调用。

作业:
1、使用C++的语法实现通讯录。
2、使用C++的语法实现五子棋。
3、使用C++的语法实现NetWork。

class MailBook
{
    const int max_cnt;
    string* name;
    char* sex;
    string* tel;
public:
    MailBook(int max_cnt):max_cnt(max_cnt)
    {
        name = new string[max_cnt];
        sex = new char[max_cnt];
        tel = new string[max_cnt];
    }
    ~MailBook()
    {
        delete[] tel;
        delete[] sex;
        delete[] name;
    }
    ...
};

int main()
{
    MailBook* mail = new MailBook(100);
    mail->start();
    delete mail;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值