C++-第二章:引用、成员函数、inline函数和auto

目录

第一节:引用

        1-1.引用的定义

        1-2.引用的注意事项

        1-3.引用的使用场景

        1-4.引用和指针的联系

第二节:成员函数

第三节:面向过程与面向对象

第四节:inline函数

第五节:auto关键字

        5-1.基本用法

        5-2.使用场景

                5-2-1.复杂类型赋值

                 5-2-2.范围for

                 5-2-3.函数

下期预告:


第一节:引用

        1-1.引用的定义

        引用的作用是给变量取别名,新的名字和旧的名字指向同一块空间,需要利用 & 符号,以int类型为例:

int a = 0;
int& b = a; // 给a取别名

        因为a、b都指向同一块空间,所以修改任何一方,另一方也会修改:

#include <iostream>
int main()
{
	int a = 0;
	int& b = a; // 给a取别名
	a = 1; // 修改a为1
	std::cout << "b=" << b << std::endl;
	b = 2; // 修改b为2
	std::cout << "a=" << a << std::endl;
	return 0;
}

 388118371315487c9fcc0fa5e8c9eccb.png

        1-2.引用的注意事项

        (1)引用必须初始化:

int& b; // 这样的写法是错误的

        (2)引用不能改变指向:

        例如上面的b是a的引用,那么b永远都只能指向a的空间,不能像指针一样改变指向。

        (3)一个变量可以有多个引用,且引用的引用还是我的引用:

int a = 0;
int& b = a;
int& c = a;
int& d = b;

        上述代码中的a三个引用b、c、d,虽然d引用的是b,但是b是a的引用,它们就指向同一块空间,所以d也是a的引用。

        1-3.引用的使用场景

        (1)作为函数参数

        作为函数参数时,引用和指针的作用类似,都是减少拷贝:

int Add(int& x, int& y)
{
	return x + y;
}
int main()
{
	int a = 1;
	int b = 2;
	Add(a,b);
	return 0;
}

        上述代码中,x就是a的引用,y就是b的引用。

        (2)代替指针使用复杂的场景

        因访问、修改指针指向空间的值都需要解引用,如果有多级指针的情况,就可能会让情况很复杂,使用引用可在一定程度上增强代码可读性。

        (3)作返回值

        作返回值时可以修改返回值,所以这个返回值需要在出函数时不销毁(全局变量,静态变量,堆上的变量等)

int a = 0;
int& func()
{
	return a; // 返回a的引用
}
int main()
{
	func() = 1; // 修改a的引用
	std::cout << "a=" << a << std::endl;
	return 0;
}

 3eeebb2ae29340f78f5fc2cf81a867a3.png

        1-4.引用和指针的联系

        引用是别名,语法上不开辟空间,指针是变量类型,要额外开空间,但是引用的底层是指针,所以实际上要开空间;

        引用必须初始化,指针可以初始化,也可以不初始化;

        引用不可以改变指向的对象,指针可以改变指向的对象;

        引用无空引用,但是有野引用;指针有空指针,有野指针;

        指针有多级指针,但是引用没有多级引用,因为一个变量的引用的引用还是它本身的引用。

        

第二节:成员函数

        在C语言的结构体中,只能够存放各种变量,但是在C++中,结构体还能存放函数:

struct calculator
{
	int a = 0;
	int Add(int x,int y)
	{
		return x + y;
	}
};

        上述代码就定义了一个结构体类型 calculator,它有一个变量a和一个函数Add,这两个都属于类 calculator的成员,如果是函数就叫成员函数,如果是变量就叫成员变量。

        那么这个成员函数如何调用呢?

        (1)我们可以定义一个结构体的实例来调用:

#include <iostream>
struct calculator
{
	int a = 0;
	int Add(int x,int y)
	{
		return x + y;
	}
};
int main()
{
	calculator cal; // 定义一个实例
	std::cout << cal.Add(1, 2) << std::endl;
	return 0;
}

 5fc8f5fbea8a44b2b751d3af91b6cb40.png

        (2)使用一个匿名实例调用函数:

int main()
{
	std::cout << calculator().Add(1,2) << std::endl;
	return 0;
}

 1d6c4f8a23134d208ea73adba24cf56a.png

        上述代码的 calculator() 就是创建了一个没有名字的实例,然后调用了它的 Add 函数,这种实例的生命周期只在当前语句,用完就可以自动销毁。

        那么为什么 类() 能够创建一个类呢,我们以后会讲。

        (3)使用类域访问静态函数

        一个结构体的类型被创建好后,可以用它定义许多实例,这些实例拥有的函数是相同的,所以为了节约空间,这些实例的函数都被存放在一块公共区域中。这些实例调用函数时实际上是去公共的空间中调用同一个函数。

        公共区域可以认为是一块命名空间,访问函数的方式和命名空间相同,但是函数需要用 static 才能够访问到:

#include <iostream>
class calculator
{
public:
	int a = 1;
	static int Add(int x,int y) // static 修饰
	{
		return x + y;
	}
};
int main()
{
	std::cout << calculator::Add(1,2) << std::endl;
	return 0;
}

  b56d738f4c1e41f3b1215d941392a826.png

        所以实际上结构体在计算大小时,不能把函数计算在内,且结构体最小为1字节:

#include <iostream>
struct calculator 
{
	int a;
	int b;
};
struct student 
{
};

int main()
{
	std::cout << sizeof calculator << std::endl;
	std::cout << sizeof student << std::endl;
	return 0;
}

 5d8113e01eee44e285135e3fea44dcbb.png

        C++中还有一个关键字 class ,它可以定义一些类类型,简称类。

        类的使用方法和结构体一模一样,类与结构体只有细微的差别,而且C++主要使用类。

class calculator // 把struct换成class
{
public: // 加上这条语句
	int a = 1;
	int Add(int x,int y)
	{
		return x + y;
	}
};

        上述代码中的public的作用以后会讲,以后我们大多数情况只使用类了。

        那么在类/结构体中添加函数有什么用呢,这就不得不讲到面向过程和面向对象编程了。

第三节:面向过程与面向对象

        面向过程:编程的主体是一个一个的步骤,每一个步骤用一个或者多个函数实现,需要的时候一个一个的调用相应的函数。C语言就是经典的面向过程的语言。

        面向对象:编程的主体是一个对象,这个对象可以完成一个或者一部分工作,这部分工作所需的函数在这个对象中,这个对象就是一个类的实例。C++就是一个面向对象的语言。

        例如现在的任务是完成多个式子的计算:1+2、2*3、8/4、3-2。我们需要用到+-*/的运算,

如果用C语言就需要写4个函数:

int Add(int x, int y)
{
	return x + y;
}
int Sub(int x, int y)
{
	return x - y;
}
int Div(int x, int y)
{
	return x / y;
}
int Mul(int x, int y)
{
	return x * y;
}

        如果使用C++,就可以把这4个函数全部变成成员函数:

class calculator // 把struct换成class即可
{
public:
	int Add(int x, int y)
	{
		return x + y;
	}
	int Sub(int x, int y)
	{
		return x - y;
	}
	int Div(int x, int y)
	{
		return x / y;
	}
	int Mul(int x, int y)
	{
		return x * y;
	}
};

        面向过程编程适合简单、短小的程序,而面向对象编程更适合大型、复杂的系统开发。

        当然,并不是说C++不能面向过程编程,实际上C++兼容C语言99%的写法,只是有了更多的选择。

第四节:inline函数

        inline函数又叫内联函数,它的特征是调用它时不建立栈帧,而是在原地展开,即让代码量更多,但是减轻栈帧的压力。

        inline函数需要用到 inline 关键字:

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

        因为inline函数原地展开的特性,决定它不合适递归函数,因为展开太多会导致可执行文件很大。

        当函数内容过多时某些编译器也不会展开它,而是像普通函数一样建立栈帧,这是某些编译器会进行的优化。

        inline函数也支持声明与定义分离:

inline int Add(int x, int y); // 函数声明
inline int Add(int x,int y)   // 函数定义
{
	return x + y;
}

        

第五节:auto关键字

        5-1.基本用法

        在python语言中,定义变量时就不需要指明变量的类型,这是因为它会自动推导变量的类型,C++中引入了auto关键字,它就可以自动推导类型:

#include <iostream>
int main()
{
	int a = 0;
	auto b = a;
	std::cout << typeid(b).name() << std::endl; // typeid().name()可得到变量的类型,以字符串的形式返回。
	return 0;
}

 32024e74f2ed49f28d6e6c3441fcba37.png

        在上述代码中,因为a的类型是int,用它初始化b时,b的类型自动被推导成int。

        其次,还可以自动推导引用类型:

#include <iostream>
int main()
{
	int a = 0;
	auto& b = a; // 引用
	std::cout << typeid(b).name() << std::endl; // typeid().name()可得到变量的类型
	return 0;
}

 8ec9d2c354c64bea9ed58224dca229f4.png

        此时b是a的引用,它们指向同一块空间,类型都是int。

        最后,auto可以加*指定为指针类型,只能传指针:

#include <iostream>
int main()
{
	int a = 0;
	auto* b = &a; // 指针
	std::cout << typeid(b).name() << std::endl; // typeid().name()可得到变量的类型
	return 0;
}

 4a6354dacfdc4dbe978ff97ddfc7d163.png

        5-2.使用场景

                5-2-1.复杂类型赋值

        在变量类型写起来很复杂时,auto就可以发挥功效了,例如给函数指针赋值时,本来需要写成:

void(*pfunc)(int,int) = func;

        使用auto就可以写成:

auto pfunc = func;

                 5-2-2.范围for

        其次auto还可以与C++的容器实现范围for:

int main()
{
	int arr[] = {1,2,3,4,5,6,7,8,9};
	for (auto num : arr) // 范围for
	{
		std::cout << num << " ";
	}
	return 0;
}

 b9d9df5601f1448daa30f52fe8c9395a.png 

        上述代码中的数组arr就是一种容器,实际上C++还引入了许多其他的容器,都可以使用范围for,这个以后会讲到;

        auto 自动推导变量num的类型(上述代码是int,如果知道元素类型用具体类型不用auto也可以),num是arr从头到尾元素的拷贝,如果用auto&就是元素别名,就可以修改元素;

        综上所述,范围for就是一种C++引入的遍历容器的手段。

        范围for的范围必须是确定的,例如将数组传参时它会变成指针,那么它的范围就不确定了,就会报错:

c254b60c86254a489f28e6289803aac2.png

                 5-2-3.函数

        在函数中,auto不能作为形参的类型:

47b7641e7e3a43a2ab70a91d94779824.png

        auto可以作为返回类型,它会自动推导返回值的类型:

#include <iostream>
auto Add(int x,int y) // auto作返回类型
{
	return x + y;
}
int main()
{
	std::cout << typeid(Add(1,2)).name() << std::endl; // typeid().name()可得到变量的类型
	return 0;
}

  00839d50993e4c6c958a4433fa03bb0f.png

        但是这种写法并不好,因为如果嵌套多个函数时,要得知最外层函数的返回值到底是什么类型就很麻烦,所以作返回值的写法不建议。

        

下期预告:

        下一次我们将具体讲讲类calss和对象,包括以下内容:

        类和对象的访问权限;

        成员函数的声明与定义分离;

        面向对象的三大特性之一:封装;

        隐含的this指针;

        类的6个默认函数;

        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值