引用的使用场景
1.引用作为函数参数
//1.引用作为函数参数
void fun1(int &a ,int &b)
{
int sum = a + b;
cout << "sum = " << sum << endl;
}
void test1()
{
int a = 10;
int b = 20;
fun1(a, b);
}
2.引用作为函数的返回值
//2.引用作为函数的返回值
int& fun2()
{
int b = 10;// 注意1:不要返回局部变量的引用
int& p = b;
return p;
}
int& fun3()
{
static int b = 10;
return b;
}
void test2()
{
/*int& q = fun2();
q = 100;
cout << q << endl;
fun2() = 200;
cout << q << endl;*/
//-------以上代码均为错误代码,只是编译器未检测出来
cout << "fun2() = " << fun2() << endl;
fun3() = 100;//注意2:如果要函数作为左值,则该函数必须返回引用
cout << "fun3() = " << fun3() << endl;
}
常量的引用
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
int main()
{
//普通引用
int a = 10;
int& ref = a;
ref = 20;
//int& ref2 = 10; //不能给字面量(1,2,3...)取别名
const int& ref3 = 10; // 可以给const修饰的引用赋予字面量
//const修饰符修饰的引用的原理
//编译器会把上面的代码变为:int tmp =10; const int &ref3 =tmp;
//ref3 =10; err 不能改动
//bool类型
//bool类型定义的变量只有两个值
bool is = 1; // 注意:is的值除0以外都是真
if (is)
cout << "真" << endl;
else
cout << "假" << endl;
return 0;
}
内联函数
- 宏函数的缺陷
- 宏函数预处理阶段被替换,不会进行类型的检测,代码安全性低
- 宏函数不能进行调试
- 容易出错,宏函数的每一部分都需要加上括号
- 每个宏函数的位置都会被展开,会造成代码的膨胀
#define ADD(x,y) x+y
//在普通函数前面加上inline是向编译器申请成为内联函数
//注意:加了inline可能成为内联函数,也可能不是
inline int Add(int x, int y)
{
return x + y;
}
void test1()
{
int a = ADD(10, 20) * 2;
cout << "a = " << a << endl;
int a2 = Add(10, 20) * 2;
cout << "a2 = " << a2 << endl;
}
#define COMPARE(x,y) ((x) < (y) ? (x) : (y))
inline int funPARE(int x, int y)
{
return x < y ? x : y;
}
void test2()
{
int m = 1;
int n = 3;
cout << "fun(m,n) = " << funPARE(++m, n) << endl;
cout << "COMPARE(x,y) = " << COMPARE(++m, n) << endl;
- 编译器不会将函数进行内联编译的情况
- 存在过多的条件判断语句
- 函数体过大
- 对函数进行取址操作
- 存在任何形式的循环语句
- 内联函数的好处
- 有宏函数的效率,但是没有宏函数的缺点
- 类的成员函数默认加上inline
函数的默认参数
1.函数的默认参数就是给函数的形参赋值
int myFun(int a, int b = 0)
{
return a + b;
}
void test1()
{
//函数的默认参数的作用
//当函数内常要用到形参的某个值,但偶尔要使用其他值
//增加函数的灵活性
cout << myFun(10, 20) << endl;
cout << myFun(10) << endl;
}
//注意1:函数的默认参数后面的参数必须全都是默认参数
int myFun2(int a, int b = 0, int c = 2, int d = 3)
{
return a + b + c + d;
}
//注意2:函数的声明和实现不能同时有函数的默认参数
void myFun3(int a, int b);
void myFun3(int a, int b = 0)
{
}
函数的占位参数
//函数的占位参数,占位参数在后面运算符重载时区分前++或后++时
void func(int a,int =10) // 占位参数也有默认值
{
}
void test2()
{
func(10);
}
函数占位参数和默认参数的混搭
void func2(int, int a = 10)
{
}
void test3()
{
func2(10);
func2(10, 30);
}
函数传参的方式
1.值传递
void swap(int a, int b)
{
int tmp = a;
a = b;
b = tmp;
}
int main()
{
int a = 10;
int b = 20;
swap(a, b);
print(a, b);
return 0;
}
2.指针传递
void swap2(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
int main()
{
int a = 10;
int b = 20;
swap2(&a, &b);
print(a, b);
return 0;
}
3.引用传递
void swap3(int& a, int& b)
{
int tmp = a;
a = b;
b = tmp;
}
int main()
{
int a = 10;
int b = 20;
swap3(a, b);
print(a, b);
return 0;
}
函数重载
1.函数重载:在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。
2.函数重载的作用:为了方便使用函数名
3.函数重载的条件:同一个作用域,参数的个数不同,参数的顺序不同,参数的类型不同
//参数的个数不同
void func()
{
cout << "func()" << endl;
}
void func(int a)
{
cout << "fun(int a) " << endl;
}
//参数的类型不同
void func(char b)
{
cout << "func(char b)" << endl;
}
//参数的顺序不同
void func(int a, double b)
{
cout << "fun(int a,double b)" << endl;
}
void func(double b, int a)
{
cout << "func(double b, int a)" << endl;
}
4.调用重载函数的注意
- 严格的类型匹配,如果类型不匹配,那么会尝试转换,转换成功就调用,失败就会报错
void test()
{
int a = 10;
double b = 3.14;
func();
//func(b); //err double没法转换成int 或 char
func(a, b);
func(b, a);
char c = 'c';
func(c); //char转换int成功,调用int参数的函数
}
5.函数重载和函数的默认参数一起使用,需要注意二义性问题
void myfunc(int a, int b = 0)
{
cout << "myfunc(int a, int b = 0)" << endl;
}
void myfunc(int a)
{
cout << "myfunc(int a)" << endl;
}
void test2()
{
//myfunc(10); //err 二义性问题,不知道调用哪个函数
}
6.函数的返回值不作为函数重载的条件
编译器是通过调用函数时,传入的参数来判断调用重载的哪个函数,而调用函数时不需要写返回值类型,所以函数的返回值不作为函数重载的条件
函数重载的原理
函数重载的原理实是在汇编时,给各个函数取别名,c语言不能重载的原因是没有取别名
C++调用C语言的函数
c++的函数在汇编时会给函数取别名,c语言不会,如果c++调用c语言的函数,c++会去宅找取了别名的函数,但是c语言没有取别名,这时会出错
test.h头文件中
#pragma once
#include<stdio.h>
//这里是告诉c++编译器,找下面的函数,要以c语言的方式去找
#ifdef __cplusplus
extern "C"
{
#endif // DEBUG
void func();
#ifdef __cplusplus
}
#endif
类和对象
1.类是自定义数据类型,是c语言的结构体进化而来的
2.对象是类实例化出来的,用数据类型定义一个变量
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Maker //类
{
public:
int a; // 成员属性(成员变量)
void func() //成员方法(成员函数)
{
cout << "func" << endl;
}
};
int main()
{
Maker m; // m是对象
return 0;
}
类的封装
1.封装是把属性(变量)和方法(函数)封装到类内,然后给这些数据赋予权限
2.封装的原因:
- 防止乱调用函数和变量,出现错误
- 维护代码更方便
3.权限
//封装:1.把属性和方法放到类中,2.给这些数据赋予权限
class Maker {
public://公共权限
void set(int Id, string Name)
{
id = Id;
name = Name;
}
void printMaker()
{
cout << "id = " << id << "\nname = " << name << endl;
}
void get()
{
cout << "Maker a = " << a << endl;
}
private://私有
int id;
string name;
protected://保护
int a;
};
//继承,公有继承
class Son :public Maker //子类:继承方式 父类
{
public:
void fun()
{
//下面这个a是从父类复制过来的
int a = 20;//子类的类内可以访问父类的保护权限的成员
}
void getS()
{
cout << "Son a= " << a << endl;
}
};
//类外不能访问私有权限的成员
//类外可以访问公有权限的成员
//类外不能访问保护权限的成员
//子类的类内可以访问父类的保护权限的成员
//类内是没有权限之分
void test()
{
Maker m;
m.set(1, "张三");
m.printMaker();
}
void test1()
{
Maker m;
m.get();
Son s;
s.fun();
s.getS();
}
4.尽量将变量属性设置为私有权限
- 可以控制属性的读写权限
- 可赋予客户端访问数据的一致性
- 可以保护属性的合法性
类和结构体的区别
结构体的成员和成员函数在默认情况下的访问级别是公有的(public),类的成员和成员函数在默认情况下的访问级别是私有的(private)。
标签:10,新手村,函数,int,void,day2,C++,重载,cout From: https://blog.51cto.com/u_16077801/7873554