C++学习笔记(2)

文章目录

    • 内联函数
      • 什么是内联函数?
      • 为什么存在内联函数?
      • 内联函数的使用场景?
      • 如果需要频繁调用一个函数,C采取什么办法进行优化
      • 有了宏为什么需要内联函数?
      • 内联函数的优点?
      • 内联函数使用的注意事项?
      • 例子
    • auto
      • 什么是auto?
      • auto使用的注意事项
    • 范围for
      • 例子一
      • 例子二
    • 空指针
      • 小结:建议初始化空指针时使用nullptr

内联函数

什么是内联函数?

用inline修饰的函数叫做内联函数

为什么存在内联函数?

没有函数压栈的开销,提高了程序的运行效率

内联函数的使用场景?

频繁调用

如果需要频繁调用一个函数,C采取什么办法进行优化

比如频繁调用Add函数

#define ADD(x,y) ((x)+(y))

宏的本质就是替换,预处理时会进行宏的替换,温馨提示/doge:定义宏时多加括号

有了宏为什么需要内联函数?

宏的缺点:

  • 不利于调试,在预处理阶段替换完成了,调试时直接看到替换后的结果

  • 可读性差,维护性差,容易出错

  • 没有类型安全的检查

宏的优点:

  • 代码复用性增强
  • 提高了性能(调用函数需要开辟栈帧之类的)

由于宏的不足,C++推荐用const、内联函数替代宏

其中const 常用来定义常量 函数定义换成内联函数

内联函数的优点?

不用开辟栈帧,在调用处直接展开函数

内联函数使用的注意事项?

  • 内联函数本质上是时间换空间

  • 代码过长,有循环,有递归等都不适合用递归(编译器会检查,如果代码过长就算用inline修饰了也会当作没修饰一样去编译),此时编译器会忽略掉内联

    内联函数一般是1-5行,编译器会自动优化,因为太长会导致代码膨胀,花费的空间过多

  • 内联函数的声明和定义放一起,不然容易出现链接错误

    -----test.h-----
    #include
    using namespace std;
    inline int Add(int x, int y);
    -----test.cpp-----
    #include"test.h"
    int Add(int x, int y)
    {
    	return x + y;
    }
    -----main.cpp-----
    #include"test.h"
    int main()
    {
    	cout << Add(1, 2) << endl;
    	return 0;
    }
    运行结果:
         error LNK2019: 无法解析的外部符号 "int __cdecl Add(int,int)" (?Add@@YAHHH@Z),该符号在函数 _main 中被引用
    原因是被inline修饰过后链接属性变化
    链接属性:external,inline,none等,感兴趣可以百度了解
    

    例子

    #include
    using namespace std;
    
    inline int Add(int x, int y)
    {
    	return x + y;
    }
    int main()
    {
    	cout << Add(1, 2) << endl;
    	return 0;
    }
    

    C++学习笔记(2)_第1张图片

auto

什么是auto?

一个类型(指示符),auto可以让编译器会自动推导变量的类型

比如 auto a=1;编译器在编译时期推导出a为int类型

(auto 通过赋值对象自动推导类型)

auto使用的注意事项

  • 必须初始化

    编译器才好推导类型

  • auto 推导指针时auto和auto*是一样的,但是声明引用时必须加&

    #include
    using namespace std;
    int main()
    {
    int a = 1;
    auto pa = &a;
    auto* pa2 = &a;
    auto& b = a;//b是a的别名
    cout << typeid(pa).name() << endl;
    cout << typeid(pa2).name() << endl;
    cout << typeid(b).name() << endl;
    
    return 0;
    }
    

    C++学习笔记(2)_第2张图片

  • auto不能用来作参数和数组声明

    假如auto去修饰形参,那变量应该开多大的空间存储?

    所以肯定不能作参数,数组同理

  • auto同一行声明的多个变量需要是同种类型

    auto a=1,b=2; 可行,因为类型相同

范围for

提供了一种新的访问数组的方式

例子一

#include
using namespace std;
int main()
{
	int arr[] = { 1, 2, 3, 4, 5 };
	for (auto e : arr)
	{
		cout << e <<" ";
	}
	cout << endl;
	return 0;
}

C++学习笔记(2)_第3张图片

例子二

用范围for把数组的每个元素乘以二

C++学习笔记(2)_第4张图片

C++学习笔记(2)_第5张图片

空指针

  • C++98把NULL定义为常量0,C++建议用nullptr表示空指针

    NULL是一个宏

    nullptr等价于 ((void *)0?

    nullptr与指针并不等价,nullptr的类型为nullptr_t,可隐式转换为任何一种指针类型

    小结:nullptr既不是整形也不是指针,只是可隐式转换为指针

源码:

stddef.h

#ifdef __cplusplus
namespace std { typedef decltype(__nullptr) nullptr_t; }
using ::std::nullptr_t;
#endif  /* __cplusplus */

这里也说明了nullptr并不是指针类型

/* Define NULL pointer value */
#ifndef NULL
#ifdef __cplusplus
#define NULL    0
#else  /* __cplusplus */
#define NULL    ((void *)0)
#endif  /* __cplusplus */
#endif  /* NULL */

其中__cplusplus是一个宏(本质上还是一个值);

上述源码的意思简要概括为:如果是C++,NULL就给成0,如果是C,就给成 ((void *)0)

借一个评论区大佬的话说:C++只有在“迫不得已的情况下才会把nullptr隐式转换成指针类型”

C++98把NULL看成0造成了一些麻烦,比如

void func(int)
{
	cout << "void func(int)" << endl;
}
void func(int*)//重载
{
	cout << "void func(int*)" << endl;
}
void func2(void*)
{
	cout << "void func(void*)" << endl;
}
int main()
{
	func(NULL);
	func(nullptr);
	func2(nullptr);

}

C++学习笔记(2)_第6张图片

C++学习笔记(2)_第7张图片

C++学习笔记(2)_第8张图片

C++学习笔记(2)_第9张图片

评论区的大佬还提到内存管理时delete nullptr更加安全,在此感谢两个大佬的指正

一开始以为nullptr就是个指针

小结:建议初始化空指针时使用nullptr

nullptr的类型不是指针,但是在“迫不得已”的情况下会隐式转换成指针

即nullptr可以转成指针,指针不一定可以转化成nullptr
C++会把NULL看成0
C语言里的NULL是((void*)0),一个指针指向内存0x00000000,这块内存不放有效数据

~~ 类与对象内容较多 打算单独记录~~

你可能感兴趣的:(C++学习笔记,c++,开发语言,后端,c语言)