C++笔记(二)核心编程

C++笔记(二)核心编程

内存分区模型
  • 代码区:存放函数体的二进制代码,由操作系统管理(存放CPU执行的机器指令)(程序运行前就有了)
    • 共享:由于会频繁的被执行,因此代码只需要存放一份共享
    • 只读:防止程序意外修改内部指令
  • 全局区:存放全局变量、静态变量和常量(字符串常量和const修饰的全局变量)(程序运行前就有了)
    • 注:const修饰的局部变量(局部常量)不在全局区中
  • 栈区:有编译器分配释放,存放函数的参数和局部变量等
    • 注:在函数体中,不要返回局部变量的地址,因为函数执行完,在栈区的局部变量会自动释放,此时返回局部变量的地址可能会返回一个乱码
  • 堆区:由程序员分配释放,若不释放,在程序结束时由操作系统回收。
    • 注:可以使用new关键字在堆区创建变量(因此,在上面栈区的注意事项中,可以在函数体的变量中用new关键字(new返回的是指针,就是地址)在堆区创建变量,那么可以返回函数中的局部变量)
//new、delete关键字的使用
double * func(){          //返回double类型的指针
    double * p = new double(10.0);  //用new创建一个double类型的数据10.0
                                    //new返回的是该数据类型的指针
    return p;                       //这样使用new将数据创建在堆区中,堆区是我们自己管理,因
                                    //此可以返回后不会释放
}
void test1(){
    double * a = func();  //使用指正接受返回值
    cout << *a << endl;   //输出10.0
    delete p;             //手动释放这块堆区内存
    //cout << *a << endl; //这时候访问返回的指针就是非法访问,会报错
}
void test2(){
    int * arry = new int[10]   //使用new创建一个数组,数组空间是10,就是10个数的数组
                               //注意定义数组使用的[]和上面定义变量使用的()
    cout << arry[9] << endl;   //这时候直接输出数组的第10个数值是没有数值的,就是这时候没有
                               //定义这个数组中的数值都是什么,可以用for循环给数组赋值
    delete[] arry;             //注意释放数组使用的[]
}
引用
基本语法
  • 引用就是给一个变量起了一个别名,这个别名与原名是一块内存,因此可以互相修改改变内存值
    • 引用的本质就是指针常量(指针的指向不可以改变),因此初始化后不可以再改变
    • 语法:数据类型 &别名 = 原名
int a = 10;           //定义一个变量
int &b = a;           //使用引用&,这样b就是a的别名
b = 20;               //这是对b进行操作,就是对a进行操作,a的值也会改变
注意事项
  • 引用必须初始化 int &b //这是错误的

    • 引用在初始化之后不可以改变b = c //b作为a的引用,不可以改变为其他的变量c的引用
  • 引用作为函数参数

    • 可以用引用的技术让形参修饰实参,达到简化指针修改实参的效果
void myswap1(int a, int b){        //使用值传递的方式
    int temp = a;
    a = b;
    b = temp;
}
void myswap2(int *a, int *b){      //使用地址传递的方式
    int temp = *a;
    *a = *b;
    *b = temp;
}
void myswap3(int &a, int &b){      //使用引用作为形参的方式
    int temp = a;                  //写法跟值传递的方式一样,但是实现效果跟地址传递一样
    a = b;
    b = temp;
}
int main(){
    int a = 10;
    int b = 20;
    myswap1();                      //值传递不会修改实参的值
    myswap2();                      //地址传递可以修改实参的值
    myswap3();                      //使用引用可以修改实参的值
    								//因为引用的别名跟原名使用的同一块内存
}
引用做函数返回值
  • 引用不可以作为局部变量的返回值
  • 使用引用作为函数的返回值时,函数的调用可以作为左值(就是说可以给这个函数的返回值赋值)
int& func(){
    static int a = 20;             //这是创建在全局区的变量,在函数执行完之后不会被释放。
    							   //因此可以作为返回值返回
    //int a = 10;                  //这是创建在栈区的局部变量,在函数执行完之后会被释放
                                   //因此不可以作为返回值返回
    return a;
}
int main(){
    int& ref = func();             //需要使用引用的变量接收函数的返回值
    func() = 1000;                 //引用的作为返回值的函数可以作为左值,成为被赋值的乙方
    cout << ref << endl;           //这时,ref输出的就是1000了
}
常量引用
  • 为了防止函数中误操作形参导致实参修改的情况(跟const在函数中的使用一样)
void func(const int &a){        //用引用作为形参,防止函数修改此形参添加const
    //a = 100;                  //因为形参a加了const,所以这里不可以修改
}
int main(){
    const int &ref = 10;        //这句可以运行,虽然并没有制定引用的原名是什么
    							//这句在编译时,会自动修改为两句:
    							//int temp = 10;  const int &ref = temp;
    ref = 20;                   //可以直接对引用操作
}
函数高级
默认参数
  • 如果有我们自己传入的数据,那么就用自己的数据,如果没有,就用默认值
  • 注:如果某个位置已经有了默认参数,那么这个位置之后,都必须有默认值
  • 注:函数的声明和实现只能有一个有默认参数
int func1(int a, int b = 10, int c = 20){        //b有默认值,那么c必须也要有默认值
}
//调用时:
func1(10);                       //因为有默认值,可以不输入带有默认值的参数
func1(10,20);                      //也可以输入带有默认参数的值,b为20 

int func2(int a = 10, int b = 20);              //函数的默认参数在声明和实现只有一个存在
int func2(int a, int b){               //函数的声明有默认参数,实现就不需要了
}
占位参数
  • 语法: 返回值类型 函数名(数据类型) {}
  • 占位参数可以有默认参数
void func1(int a, int){}             //形参中的int,就是占位参数
                        //后面会用到这个占位参数
void func2(int a, int = 10){}     //占位参数可以有默认参数
int main(){
    func(10,10);             //占位参数没有变量,但是依然需要传入参数
    func(10);
}
函数重载
  • 可以让函数名相同,提高复用性
  • 函数重载的条件
    • 同一个作用域下
    • 函数名称相同
    • 函数的参数类型(int和const int也是不同的)不同,或者数量不同或者,顺序不同
    • 返回值的类型不同不能作为函数重载的条件(换句话说,函数名可以相同,但是要根据参数让编译器分辨出你要执行的是那个函数)
void func(){}             //函数重载名称可以相同,但是参数不能相同
void func(int a){}
void func(double a, int b){}
int func(double a, int b){}        //不能将返回值作为重载的条件,因为编译器识别不出来
int main(){
    func();               //执行的时候编译器会根据参数的不同使用不同的函数
    func(10);
    func(10.0,10);
}
  • 函数重载注意事项
    • 引用作为重载的条件
void func(int &a){}                 //引用作为重载
void func(const int &a){}
int main(){
    int a = 10;
    func(a);               //这句会运行第一个函数,因为a是一个变量
    func(10);              //这句会运行第二个函数
    //对第一个函数来说,实参给到形参相当于int &a = 10,不合法
    //对第二个函数来说,实参给到形参相当于const int &a = 10,合法,在引用那里找,这句是合法的
}
    • 函数重载碰到默认参数
void func(int a, int b = 10){}       //尽量避免使用带有默认参数的函数重载
void func(int a){}
int main(){
    func(10,20);             //可以运行
    func(10);               //会出错,编译器不知道你要运行哪个
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值