C++学习笔记(1)

本文详细介绍了C++中的引用特性,包括引用的初始化、引用作为参数的使用,以及引用与指针的区别。此外,还探讨了模板的概念,如模板函数的格式、实例化、实参推演和类型形参转换。最后提到了函数模板的特化和重载。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.  内联函数
内联函数是指用inline关键字修饰的函数。在类内定义的函数被默认成内联函数。内联函数从 源代码层看,有函数的结构,而在编译后,却不具备函数的性质。
内联函数不是在调用时发生控制转移,而是在编译时将函数体嵌入在每一个调用处。编译时,类似宏替换,使用 函数体替换调用处的函数名。一般在代码中用inline修饰,但是能否形成内联函数,需要看 编译器对该函数定义的具体处理。
内联扩展是用来消除 函数调用时的时间开销。它通常用于频繁执行的函数,对于小内存空间的函数非常受益。
如果没有内联函数, 编译器可以决定哪些函数内联 。程序员很少或没有权利控制哪些只能是内联的,哪些不可以内联,作用是程序员可以选择内联的特定应用 。
1.递归函数不能定义为内联函数
2.内联函数一般适合于不存在while和switch等复杂的结构且只有1~5条语句的小函数上,否则编译系统将该函数视为普通函数。
3.内联函数只能先定义后使用,否则编译系统也会把它认为是普通函数。
4.对内联函数不能进行异常的接口声明。
内联函数具有 一般函数 的特性,它与一般函数所不同之处只在于 函数调用 的处理。一般函数进行调用时,要将程序执行权转到 被调用函数 中,然后再返回到调用它的函数中;而内联函数在调用时,是将调用 表达式 用内联函数体来替换。在使用内联函数时,应注意如下几点: 1.在内联函数内不允许用 循环语句 和开关语句。 如果内联函数有这些语句,则编译将该函数视同普通函数那样产生函数调用代码,递归函数(自己调用自己的函数)是不能被用来做内联函数的。内联函数只适合于只有1~5行的小函数。对一个含有许多语句的大函数,函数调用和返回的开销相对来说微不足道,所以也没有必要用内联函数实现。 2.内联函数的定义必须出现在内联函数第一次被调用之前。 3.本栏目讲到的类结构中所有在类说明内部定义的函数是内联函数。

二.引用

引用引入了对象的一个同义词。定义引用的表示方法与定义指针相似,只是用&代替了*。引用(reference)是c++对c语言的重要扩充。引用就是某

一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。其格式为:类型 &引用变量名 = 已定义过的变量名。

引用的特点:

①一个变量可取多个别名。

②引用必须初始化。

③引用只能在初始化的时候引用一次 ,不能更改为转而引用其他变量。



①基础引用:

[cpp]  view plain  copy
  1. void TestReference1 ()  
  2. {  
  3.      int a = 1;  
  4.      int& b = a;  
  5.   
  6.      cout<<"a:address->" <<&a<< endl;  
  7.      cout<<"b:address->" <<&b<< endl;  
  8.   
  9.      a = 2;  
  10.      b = 3;  
  11.      int& c = b;// 引用一个引用变量,别名的别名  
  12.      c = 4;  
  13. }  



②const引用:

[cpp]  view plain  copy
  1. void TestReference2 ()  
  2. {  
  3.      int d1 = 4;  
  4.      const int & d2 = d1;  
  5.      d1 = 5;//d1改变,d2的值也会改变。  
  6.      //d2 = 6;//不能给常量(不能被修改的量)赋值。  
  7.   
  8.      const int d3 = 1;  
  9.      const int & d4 = d3;  
  10.      //int&d5 = d3;  
  11.      const int & d6 = 5;//常量具有常性,只有常引用可以引用常量  
  12.   
  13.      double d7 = 1.1;  
  14.      //int& d8 = d7;//d7是double类型,d8是int,d7赋值给 d8时要生成一个临时变量  
  15.                    //也就是说d8引用的是这个带有常性的临时变量,所以不能赋值。  
  16.      const int& d9 = d7;  
  17. }  


③引用作参数:

[cpp]  view plain  copy
  1. 1.【值传递】如果形参为非引用的传值方式,则生成局部临时变量接收实参的值  
  2. void Swap (int left, int right) //值传递的方式无法实现交换,因为传参时对于参数left和right拷贝一临时副本,交换的是副本值,因为其是临时变量函数退出,变量销 {                                //毁,并不会影响外部left和right的值。  
  3.      int temp = left;  
  4.      left = right ;  
  5.      right = temp ;  
  6. }  
  7.   
  8. 2.【引用传递】如果形参为引用类型,则形参是实参的别名。  
  9. void Swap (int& left, int& right)//使用引用的话,不做临时拷贝,&的使用说明此处只是原参数的另一个名字而已,所以修改时直接在原参数的基础上修改变量值。  
  10. {  
  11.      int temp = left;  
  12.      right = left ;  
  13.      left = temp ;  
  14. }  
  15.   
  16. 3.【指针传递】  
  17. void Swap (int* pLeft, int* pRight)//传入的是地址,因为地址是唯一的,所以指针通过地址的访问进而可修改其内容。  
  18. {  
  19.      int temp = *pLeft;  
  20.      *pLeft = *pRight;  
  21.      *pRight = temp;  
  22. }  



引用虽方便,使用须谨慎:

(1)&在这里不是求地址运算,而是起标识作用。

(2)类型标识符是指目标变量的类型。

(3)声明引用时,必须同时对其进行初始化。

(4)引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。

(5)对引用求地址,就是对目标变量求地址。即引用名是目标变量名的一个别名。引用在定义上是说引用不占据任何内存空间,但是编译器在一般将

其实现为const指针,即指向位置不可变的指针,所以引用实际上与一般指针同样占用内存。

(6)不能建立引用的数组。因为数组是一个由若干个元素所组成的集合,所以无法建立一个由引用组成的集合,但是可以建立数组的引用。

(7)引用常见的使用用途:作为函数的参数、函数的返回值。



总结:

1. 不要返回一个临时变量的引用。

2. 如果返回对象出了当前函数的作用域依旧存在,则最好使用引用返回,因为这样更高效。

* 引用和指针的区别和联系(
笔试热点

1. 引用只能在定义时初始化一次,之后不能改变指向其它变量(从一而终);指针变量的值可变。

2. 引用必须指向有效的变量,指针可以为空。

3. sizeof指针对象和引用对象的意义不一样。sizeof引用得到的是所指向的变量的大小,而sizeof指针是对象地址的大小。

4. 指针和引用自增(++)自减(--)意义不一样。

5. 相对而言,引用比指针更安全。


指针比引用更为灵活,但是其风险也很大。使用指针时一定要检查指针是否为空(NULL),且空间回收后指针最好置

零,以免野指针的发生造成内存泄漏等问题




Ⅰ.引用和指针的区别和联系:

不同点

1. 指针是一个实体,而引用仅是个别名;

 2. 引用使用时无需解引用(*),指针需要解引用;

 3. 引用只能在定义时被初始化一次,之后不可变;指针可变;

 4. 引用没有 const,指针有 const;const修饰的指针不可变;

 5. 引用不能为空,指针可以为空;

 6. “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小;

7. 指针和引用的自增(++)运算意义不一样;

 8.从内存分配上看:程序为指针变量分配内存区域,而引用不需要分配内存区域。

★相同点两者都是地址的概念,指针指向一块儿内存,其内容为所指内存的地址;引用是某块儿内存的别名。





Ⅱ.const在C和C++中的含义(笔试热点):

⑴C中的const,功能比较单一,较容易理解
作用:被修饰的内容不可更改。
使用场合:修饰变量,函数参数,返回值等。(c++中应用场合要丰富的多)
特点: 是运行时const,因此不能取代#define用于成为数组长度等需要编译时常量的情况。同时因为是运行时const,可以只定义而不初始化,而在运行时初始化。如 const int iConst;。 另外,在c中,const变量默认是外部链接,因此在不同的编译单元中如果有同名const变量,会引发命名冲突,编译时报错。
⑵c++中的const:

a、非类成员const:

①const变量默认是内部连接的,因此在不同的编译单元中可以有同名的const 变量定义。

②编译时常量,因此可以像#define一样使用,而且因为上面一点,可以在头文件中定义const变量,包含的不同的cpp文件(编译

单元)中使用而不引起命名冲突。

③编译器默认不为const变量分配内存,除非:1. 使用 extern 申明, 2:程序中有引用const 变量的地址。

④c++中临时对象/内置变量默认具有const属性。

b、类中的const:

①与c语言中的const一样,只是运行时常量,不能作为数组维数使用,即不能取代#define。在类中使用下面两种方式取代#define: 1:static const... 

2 : enum{....}//enum 不占存储空间。

②类中的const 变量占用存储空间。

③类中的const成员变量需要在构造函数初始化列表中初始化。

④const 对象:在该对象生命周期内,必须保证没有任何成员变量被改变。const对象只能调用const成员函数。

⑤const成员函数: void fun() const ... 不仅能被const对象调用,也能被非const对象调用,因此,如果确认一个任何成员函数不改

变任何成员变量,应该习惯性将该函数定义成const类型。

⑥如果一个对象被定义成const,那么该const对象“可能”会被放入到ROM当中,这在嵌入式开发当中有时非常重要

三.函数模板

泛型编程

编写与类型无关的逻辑代码,是代码复用的一种手段。模板是泛型编程的基础

模板

代表一个函数家族,该函数与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本(它本身不是类或函数)

模板函数的格式

这里写图片描述

模板的实例化

产生模板特定类型的过程称为函数模板的实例化

template <typename T>
T Add(T left,T right)
{
    return left + right;
}
int main()
{
    Add(1,2);
    Add(1.0,2.0);
    Add('1','1');
    Add(1,1.0);//error:模板参数“T”不明确  
    //模板函数不会进行类型转换
    Add<int>(1, '1');//显式实例化
    Add<double>(1, 1.0);
    Add(1,(int) 1.0);//隐式实例化
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
模板的实参推演

从函数实参确定模板形参类型和值得过程称为模板实参推断

类型形参转换

一般不会转换实参已匹配以有的实例化,相反会产生新的实例; 
编译器只会执行两种转换:1.const转换;2.数组或函数到指针的转换

  • const转换

    接收const引用或const指针的函数可以分别用非const对象的引用或者指针来调用

  • 数组或函数到指针的转换

    如果模板形参不是引用类型,则对数组或函数类型的实参应用常规指针转换。数组实参将当做指向其第一个元素的指针,函数实参当做指向函数类型的指针

模板形参
  • 模板类型参数
typedef double T;
//模板形参的名字在同一模板中只能使用一次
template <typename T, typename T>// error C2991: 重定义 模板 参数“T”
//所有模板形参前必须加上class/typename关键字修饰
template <typename T,U>//error C2061: 语法错误: 标识符“U”
template <typename T = int> //可以指定缺省的模板实参
T Add(T left=1, T right=2 )//缺省的调用参数
{
    cout << "T type = " << typeid(T).name() << endl;//打印模板参数T的类型
    return left + right;
}
T val;//全局变量
int main()
{
    Add();
    cout << "T type = " << typeid(T).name() << endl;//打印全局变量的类型
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

这里写图片描述

模板形参的名字只能在模板形参之后到模板声明或定义的末尾使用

  • 非模板类型参数 
    模板内部定义的常量
template <typename T = int ,int N = 10>  //N:非模板类型参数
void Funtest(T(&_array)[N])   //数组引用
{
    for (int i = 0; i < N; ++i)
    {
        _array[i] = 0;
    }
}
int main()
{
    const int c = 3;
    int a[4];
    int b[c + 1];  
    Funtest(a);
    Funtest(b);
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
模板函数和同名非模板函数
int Max(const int& a, const int& b)   //同名的非模板函数和模板函数可以同时存在
{
    return a > b ? a : b;
}
template <typename T>
T Max(const T& a,const T& b)
{
    return a > b ? a : b;
}
template <typename T1, typename T2>
T1 Max(const T1& a, const T2& b)
{
    return a > b ? a : b;
}
int main()
{
    Max(1, 2);//优先调用非模板函数
    Max<>(1, 2);//显示指定,只能调用模板
    Max(1, 2.0);//优先调用优化模板template <typename T1, typename T2>
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
模板特化
  • 特化格式 
    这里写图片描述
template <typename T>//模板实例
int compare(T a,  T b)//比较了地址
{
    if (a < b)
        return -1;
    if (a>b)
        return 1;
    return 0;
}
template <>//模板特化    在模板实例之后
//特化的声明必须与特定的模板相匹配
//int compare<const char*>(char* p1, char*  p2)//error :不是函数模板的专用化
int compare<const char*>(const char* p1, const char*  p2)
{
    return strcmp(p1,p1);
}
int main()
{
    char* p1 = "abcd";
    char* p2 = "abce";
    const char* p3 = "abcd";
    const char* p4 = "abce";
    cout << compare(p1, p2) << endl;
    cout << compare(p3, p4) << endl;
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
模板重载
template <typename T>
T Max(const T& a, const T& b)
{
    return a > b ? a : b;
}
template <typename T>
T Max(const T& a, const T& b, const T& c)//重载
{
    return Max(Max(a, b), c);
}
int main()
{
    Max(1, 2);
    Max(1, 2, 4);
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值