一. c++中的引用
引用: 给一个已知的变量起一个别的名字,内存不会为其分配新的内存地址 ---- 节省内存空间
1.语法
数据类型 &引用名 = 引用的变量名
C++ int a = 10; int &b = a; // 此时的a和b是完全等价 //定义函数 交换两个变量的值 void swap1(int m,int n){ cout << "交换前mn的结果:" << m << ":" << n << endl; int tmp = m; m = n; n = tmp; cout << "交换后mn的结果:" << m << ":" << n << endl; } //定义函数 交换两个变量的值--参数是指针类型 void swap2(int * m,int * n){ int tmp = *m; *m = *n; *n = tmp; } //定义函数 交换两个变量的值--参数是引用类型 void swap3(int &m,int &n){ int tmp = m; m = n; n = tmp; } |
2.使用引用传参数好处
在传参过程中,不会分配新的变量空间----节省内存空间
使用场景: 一般用在函数传参中,让函数直接访问数据本身
3.常量引用
1.语法
const 数据类型 &引用变量名 = 引用的对象
举例:
C++ int a = 12345; // 常量引用 const int &b = a; // b 无法修改 a的值 |
2.作用
1 防止引用名修改原来变量的值(防止原来的数据被修改)
2 对数据常量进行引用
4.常量引用作为函数的参数
当引用作为参数传递时, 用户不需要修改该数据时, 传递常量引用
C++ // 定义函数 传递引用参数 -- 数据不需要修改 常量引用 int add(const int &m,const int &n){ return m + n; } int main(int argc, char const *argv[]) { int a = 40 ; int b = 20; cout << add(a,b) << endl; //既可以传递变量 cout << add(100,200) << endl; //也可以传递常量 } |
补充:
常量引用的特殊用法:
数据类型 &&引用名 = 引用的对象;
举例:
C++ int &&a = 1000; //只能引用常量 不能引用变量 //特殊用法--&& 只能引用常量 不能引用变量 int sub(int &&x, int &&y){ return x - y; } int main(int argc, char const *argv[]) { //cout <<sub(a,b) <<endl; //不能引用变量 cout <<sub(34,23) <<endl; //只能引用常量 } |
5.引用作为函数的返回值
注意: 使用引用作为函数的返回值, 必须要保证函数结束之后, 引用对象的空间必须存在!!
C++ //2 定义全局变量 // int sum = 10086; //定义函数 // 引用作为函数返回值使用 int &add(){ // int sum = 10086; // sum局部变量 函数执行结束 会被系统回收 // 引用时 sum已经被系统释放了 所以不能作为引用对象 //解决:将返回值从栈空间转为数据段存储 //1. static 修改为静态变量 static int sum = 10086; //static只初始化一次 //2. sum = 10086; return sum; } int main(int argc, char const *argv[]) { // cout << add() << endl; //将变量sum起别名 int &a = add(); a = 10010; cout << add() <<endl; //使用第一种方式: 10010 //static只初始化一次 //使用第二种方式: 10086 输出add()就又重新调用函数 return 0; } |
总结:
引用作为函数返回值时,一般可以返回:
静态变量,全局变量,堆空间.... 不会被释放的空间就可以!!
6.引用作为函数返回值时 函数可以作为左值
- 左值和右值
int a = 10;
等号左侧 左值 -- 左值一般是变量
等号右侧 右值 -- 右值一般是常量 也可以是变量
C++ //引用返回 int &res2(){ static int sum = 100; return sum; } main(){ cout << res2() << endl; //函数 当做右值处理 -- 没有问题 int a = res2(); // int &a = res2(); // cout << a << endl; //函数 当做左值处理 -- 没有问题 res2() = 300; // cout << a << endl; // a = 400; cout << res2() << endl; } |
总结: 引用作为函数返回值时 函数可以作为左值
7.引用的注意事项
- 引用必须要初始化(引用就是起别名,没有对象就没有起名的意义)
- 引用常量,需要使用const常量引用方式 或 && 右值引用
- 引用的类型必须匹配
- 当引用初始化之后,不能再次修改(和指针不同,因为指针可以修改)
一个别名(引用)只能表示一个人(初始化之后不能被再次修改)
一个人可以有多个别名(一个引用对象可以被多次引用)
C++ //1 定义空引用是错误的!!!必须初始化!! // int &a; /* //2 引用常量 //方式1 // int &a = 100; //错误 const int &a = 100; //方式2 &&右值引用 int &&b = 200; cout << b << endl; */ /* //3 引用类型必须匹配 int m = 123; int &n = m; // char &k = m; //错误 引用类型不匹配 //练习 为p和str 取一个引用 int * p = &m; char str[100] = {"hello"}; int * &x = p; char (&y) [100] = str; */ //4 当引用初始化之后 无法再次修改 int f = 100; int g = 200; // int *ptr = &f; // 指针指向f的地址 // ptr = &g; // 指针指向g的地址 int &yy = f; //初始化 yy = g; //想让yy重新引用b --无效 // 相当于单纯给yy赋值 地址没有修改 cout << &f <<endl; cout << &g <<endl; cout << &yy << endl; //输出的是yy重新赋的值 cout << yy << endl; // f也会发生改变 cout << f << endl; int &xx = f; cout << &xx << endl; |
二. C++中的多态
多态: 一个对象 作用于不同的事物,得到的结果不同
人 ----> 卧室的床 ----> 睡觉状态
人 ----> 教室的桌上 -->学习状态
人 ----->网吧的电脑 ----> 玩游戏的状态
提高代码的复用性!!!
优点: 设计一个接口, 实现不同的功能
静态多态:程序在编译时,已经确定要执行的状态
动态多态:程序在运行时,才能确定要执行的状态
C++中 函数重载 就相当于 静态多态
函数重载---一个函数可以实现不同的功能
三. 函数重载
1.函数重载的定义
1 相同函数名 定义不同功能 称为函数重载
2 重载的函数:根据参数的个数,参数类型进行区分
C++ int pf(){ cout << "打印一个整型数据:" << a << endl; return 0; } //函数重载 int pf(double b){ cout << "打印一个浮点型数据:" << b << endl; return 0; } int pf(char c){ cout << "打印一个字符:" << c << endl; return 0; } int main(int argc, char const *argv[]) { //调用函数 pf(123); pf(12.3); pf('M'); return 0; } |
2.函数重载的原理
- C++编译器在编译时会自动检查用户在编写函数时的参数列表,根据参数列表中参数的不同(个数,类型)对函数名别名化,从而产生多个名称不同的函数,
- 函数调用时,根据用户传入的参数来自动推导要执行哪个函数
练习: 实现两个数据相加(不要失去精度)
C++ extern "C"{ #include <stdio.h> } //整型相加 int add(int a, int b){ return a+b; } //浮点型相加 double add(double a, double b){ return a+b; } //字符串相加--连接 char * add(const char * str1,const char * str2){ static char str[100] = {0}; sprintf(str,"%s %s",str1,str2); return str; } //计算两个数据相加 (不失精度) int main(int argc, char const *argv[]) { cout << add(10,20) <<endl; cout << add(10.12,20.34) <<endl; cout << add("hello","world") <<endl; return 0; } |
函数重载的注意事项:
1.函数重载时函数名必须相同
2.函数重载的依据是 : 参数个数和参数的类型 不一样
3 返回值类型不能作为重载的依据
4 调用重载函数时 防止歧义产生(有可能形参有默认值)
3.函数的默认参数
默认参数: 在c++中定义函数时,可以使用默认参数进行形参赋值
C++ //int a = 100就是默认参数 int pf(int a=100){ return a; } 作用: 简化函数的调用 例如:mmap void my_mmap(int fd,size_t length ,void *addr=NULL, int prot = PROT_READ|PROT_WRITE, int flags=MAP_SHARED,off_t offset = 0){ return mmap(fd,length ,addr, prot, flags,offset); } |
注意:
定义函数时, 默认参数赋值从右往左(有参数值的往右放,没参数值的往左放)
调用函数时,实参传递给形参是从左往右
十. 面向对象与面向过程
面向过程--洗衣服:
1.我找到脏衣服,
2.打开洗衣机,
3.将衣服放进洗衣机中
4.倒入洗衣液
5.洗衣机转动洗衣服
6.洗衣机甩干衣服
7.我将衣服拿出来晾晒
面向对象 -- 解决办法
抽离两个对象: 我这个人 洗衣机
对象:人
1 人.找脏衣服
2 人.打开洗衣机
3 人.倒入洗衣液
4.人.拿衣服晾晒
对象:洗衣机
1 洗衣机.洗衣服
2.洗衣机.甩干衣服
面向过程: find.c wash.c
面向对象: class find, class show
总结一句话: 面向对象就是将面向过程中功能代码想办法都写入到类中
四. c++中的类(核心)
1.类的三大特征: 封装 继承 多态
- 封装:
- 继承 :
优点:
子类直接继承父类的接口函数, 就不需要重新开发接口
提高了代码的复用性 有利于软件版本的升级
- 多态
- 一个对象作用于不同的事物,所得到的结果(功能)不同
优点: 提高了代码的复用性
2.类的定义
语法:
C++ class 类名{ 成员列表; 方法.... } |
以上类的定义是最简单的定义方式,没有什么意义,默认成员和方法都是私有的,外界无法访问
需要修饰符来修饰成员和方法:
public 公有的 ------- 外界可以访问
protected 受保护的 ------- 外界不可以访问
private 私有的 ------- 外界不可以访问
五. C++中的构造函数
构造函数:
1 函数名和类名相同
2 函数没有返回值
3 函数不需要用户调用,创建对象的时候自动调用
构造函数作用: 用于定义对象时,初始化对象的数据成员
语法:
C++ class 类名{ public: 类名(){ //构造函数 } } |
案例:
C++ //定义类 class base{ public: // 构造函数必须写在public公共区 // 否则 无法创建对象 //构造函数----函数名和类名同名 //无参构造函数 base(){ cout << "创建对象,自动调用构造函数" << endl; } //带参构造函数 base(int a){ num = a; cout << num << endl; } private: int num; }; int main(int argc, char const *argv[]) { //定义对象调用构造函数 -- 无参 base a; //定义对象时传入参数 -- 带参 base b(10086); return 0; } |
注意:
- 构造函数必须写在公共区public里, 因为定义对象时会自动调用, 只有在公共区才可以被外界直接访问调用
- 假设用户没写任何的构造函数,系统会自动生成无参构造函数
练习: 设计一个学生类,公有成员姓名name(char *),保护成员money(double),私有成员密码pwd(int)
C++ 定义一个构造函数初始化这些数据成员 并写一个输出接口 #include <iostream> using namespace std; extern "C"{ #include <string.h> } /* 设计一个学生类, 公有成员姓名name(char *), 保护成员money(double), 私有成员密码pwd(int) 定义一个构造函数初始化这些数据成员 并写一个输出接口 */ class Stu{ public: char name[40]; //解决方式1: 函数重载 // Stu(){ // cout << "构造函数被调用" << endl; // } //正常步骤 // Stu(const char * n,double m, int p){ // cout << "构造函数被调用" << endl; // //初始化 // strncpy(name,n,strlen(n)+1); // money = m; // pwd = p; // } //解决方式2: 默认参数 //构造函数 Stu(const char * n="ZQ",double m=1.34, int p=123456){ cout << "构造函数被调用" << endl; //初始化 strncpy(name,n,strlen(n)+1); money = m; pwd = p; } void show(){ cout << "name:" << name << endl; cout << "money:" << money << endl; cout << "pwd:" << pwd << endl; } protected: double money; private: int pwd; }; int main(int argc, char const *argv[]) { //定义对象 -- 必须传参 Stu obj("ZQ",12345567.8,123456); //调用接口 obj.show(); //想法: 创建对象时 不传递参数 -- 简化 // 解决方式1: 函数重载 // Stu pp; //解决方式2: 默认参数 Stu pp; return 0; } |
总结:
构造函数支持函数重载