C++对c的拓展之, 引用和const关键字

bool类型关键字

C++中的布尔类型

C++在C语言的基本类型系统之上增加了bool

C++中的bool可取的值只有true和false

理论上bool只占用一个字节,

如果多个bool变量定义在一起,可能会各占一个bit,这取决于编译器的实现

true代表真值,编译器内部用1来表示

false代表非真值,编译器内部用0来表示

#include <iostream>
using namespace std; int main()
{
bool b1 = true; // 告诉c++编译器给我分配一个字节内存
cout << sizeof(b1) << endl;
// bool 只有真或者假(0或非0) true或false
b1 = ;
cout << b1 << endl; // 还是为1
return ;
}
结果:

三目运算符

三目运算符在C语言中是右值,右值不可以被赋值。但在C++中,三目运算符是左值,左值可以赋值。

左值和右值:

左值:可以放在赋值操作符左边的是左值,左值为Lvalue,L代表location,表示内存可以寻址,可以赋值。

右值:右值为Rvalue,R代表read,不可以被赋值。

#include <iostream>
using namespace std; int main()
{
int a = ;
int b = ;
// a > b ? a : b = 100; // 在c++中三目运算可以当左值, 思考在c语言中如何实现的
// 实现方式: *(a > b ? &a : &b) = 100;
a > b ? a : b = ;
cout << a << " " << b << endl;
return ;
}

三目运算符在c语言中如何实现当左值

#include <stdio.h>

int main()
{
int a = ;
int b = ;
*(a > b ? &a: &b) = ;
printf("a = %d, b = %d ", a, b);
return ;
} // 结果
a = 10, b = 100 

可以看出,c++编译器帮我们做了一个&

const关键字

const int a = ;
int const b = ;

这两种写法是等价的,都是表示变量的值不能被改变,需要注意的是,用const修饰变量时,一定要给变量初始化,否则之后就不能再进行赋值了,而且编译器也不允许不赋初值的写法

    const int a;
int const b; // a和b都是不可以修改的 const int *c; // 常量指针, c是本身可以修改, 但是他指向的值不可以修改
int * const d; // 指针常量: d这个指针本身是一个常量不可以修改,但是他指向的值可以修改
const int * const e ;// 指针e本身和他指向的值都不可以修改
#include <iostream>
using namespace std; int main()
{
const int a = ;
int const b = ; //这两个一样 int i = ;
int *const p1 = &i; // 不能改变p1的值,这是一个顶层const
const int ci = ; // 不允许改变ci的值,这是一个顶层const
const int *p2 = &ci; // 不允许改变p2的值,这是一个底层const
const int *const p3 = p2; // 靠右的const是顶层const,靠左的是底层const
const int &r = ci; // 用于声明引用的const 都是底层const
// const int *const e
// 顶层const:表示指针本身是个常量
// 底层const:表示指针所指对象是一个常量 // c语言中const是个“冒牌货”,下面的代码在c文件下可以修改t的值
// c++中const是一个真正的常量
// 造成这样原因是:c++编译器对const进行了拓展 const int t = ;
int *p = NULL;
p = (int*)&t;
*p = ;
cout << t; // 还是10
// 但是*p是20
}

const例子

Const好处
//合理的利用const,
//1指针做函数参数,可以有效的提高代码可读性,减少bug;
//2清楚的分清参数的输入和输出特性
int setTeacher_err( const Teacher *p) p这个指针指向的值不可以被修改
Const修改形参的时候,在利用形参不能修改指针所向的内存空间
const 编译时进行分配内存 c和c++中const
结论:
C语言中的const变量
C语言中const变量是只读变量,有自己的存储空间
C++中的const常量
可能分配存储空间,也可能不分配存储空间
当const常量为全局,并且需要在其它文件中使用
当使用&操作符取const常量的地址

c和c++中const的区别

const 和define

对比加深
C++中的const常量类似于宏定义
const int c = ; ≈ #define c 5
C++中的const常量与宏定义不同
const常量是由编译器处理的,提供类型检查和作用域检查
宏定义由预处理器处理,单纯的文本替换
#include <iostream>
using namespace std; void fun1()
{
#define a 10
const int b = ;
//#undef a # undef
} void fun2()
{
printf("a = %d\n", a);
//printf("b = %d\n", b);
} int main()
{
fun1();
fun2();
cout << a << endl;
return ;
}

总结

C语言中的const变量
C语言中const变量是只读变量,有自己的存储空间
C++中的const常量
可能分配存储空间,也可能不分配存储空间
当const常量为全局,并且需要在其它文件中使用,会分配存储空间
当使用&操作符,取const常量的地址时,会分配存储空间
当const int &a = 10; const修饰引用时,也会分配存储空间 const和define 详细介绍参考博客, 写的比较全
https://www.cnblogs.com/33debug/p/7199857.html

引用

为什么要有引用:

在c语言中传递参数的方式有值传递,址传递,如果想保存的参数的修改,就用址传递,如果不想保存就用值传递

但是很多时候址传递太危险了,易出现内存方面的各种问题,所以就有了引用

什么是引用:

变量名实质上是一段连续存储空间的别名,是一个标号(门牌号) 
引用可以看作一个已定义变量的别名
引用的语法:Type& name = var;
引用(reference)不是新定义一个变量, 而是给已存在的对象取了 一个别名 ,引用类型,引用另外一种类型。 编译器不会为引用对象新开辟内存空间, 它和它引用的对象共用同一块内存空间 。
一般在初始化变量时,初始值会被拷贝到新建的对象中。在定义引用时程序把引用和他的初始值绑定在一起,而不是将初始值拷贝给引用。
一旦初始化完成引用就将和他的初始对象一直绑定在一块(同生共死)。你无法将引用重新绑定到另外一个对象上。
可以通过引用来修改变量的值
int num = ;
int &number = num; //number指向num(是num的另外一个名字)
    void swap (int& left, int& right)
{
int temp = left;
left = right;
right = temp;
}

简单示例

引用的定义方式:

(1)允许在一条语句中定义多个引用,其中每个引用标识符都必须以&开头;

(2)因为无法将引用重新绑定到另外一个对象上,因此引用必须初始化。

(3)因为引用本身不是一一个对象,所以不能定义引用的引用。

(4)一个变量可以有多个引用,而一个引用只能给一个变量对象 。

(5)引用的类型要和与之绑定的对象严格匹配(不严谨)。

(6)引用只能绑定在对象上而不能和字面值或某个表达式计算的过程绑定在一起。

int i1 = , i2 =  ;     //i1和i2都是int型
int &r1 = i1, &r2 = i2; //r1和r2都是引用
int &r3 ; //报错:引用必须初始化
int &r4 = i1, &r5 = i2; //r1, r4同为i1的引用,r2, r5同为i2的引用
int &r4 = i2, &r5 = i1; //报错:r4不能同时分别为i1和i2的引用
int &r6 = ; //报错:引用类型的初始值必须是一个对象
double i3 = 3.14;
int &r7 = i3; //报错:此处引用类型的初始值必须是int型对象

定义方式

引用的本质

引用和其所引用的变量是同一个地址证明

#include <iostream>
using namespace std; // 1第一点,单独定义引用时,必须初始化,说明像一个常量
int main()
{
int a = ;
int &b = a; // b很像一个常量 // 地址相同说明,a,b均是同一块内存的门牌号
cout << "&a=" << &a << "\n&b=" << &b << endl;
return ;
}
#include <iostream>
using namespace std; void modifyA(int &a)
{
a = ;
}
// A函数的内部就是执行的A2
void modifyA2(int* const a)
{
*a = ;
}
//3 引用的本质,c++编译器帮我们进行取地址
int main()
{
int a = ;
modifyA(a); // 执行这个函数时 程序员不需要进行取地址
cout << "a :" << a << endl; modifyA2(&a);// 如果是指针需要手动取地址
cout << "a2 :" << a << endl; return ;
}

引用本质二:帮我们取地址

#include <iostream>
using namespace std; // 普通引用有自己的空间吗? 有
struct Teacher
{
char name[]; //
int age; //
int &a; // 4 很像指针,所占空间大小
double &b; //
}; int main()
{
cout <<"sizeof(Teacher)=" << sizeof(Teacher)<< endl;
return ;
}

引用有自己的空间

函数返回值为引用  匿名对象

当函数返回值为引用时
若返回栈变量 (临时对象,变量)
不能成为其它引用的初始值
不能作为左值使用
若返回静态变量或全局变量
可以成为其他引用的初始值
即可作为右值使用,也可作为左值使用
C++链式编程中,经常用到引用,运算符重载专题

 注意 临时对象不能返回引用或者指针

当临时对象离开其作用域后,他的内存就释放了

#include <iostream>
using namespace std; // 返回a的本身,返回a的副本
int getA1()
{
int a;
a = ;
return a;
} // 临时变量不能用返回其引用或者指针
// 因为当函数结束时,该变量就被释放了
// 所以getA2 和 A3是错误的
// 当是一个类的对象时,更要注意
int& getA2()
{
int a; // 如果返回的是一个栈上的引用(局部变量),可能会有问题
a = ;
return a;
} int* getA3()
{
// 若返回栈变量,不能其他引用的初始值
int a;
a = ;
return &a;
}
int main()
{
int a1 = getA1();
int a2 = getA2();
// int &a22 = getA2(); // 不同的编译器,结果可能不同(乱码)意味着可能会有bug cout << "a1=" << a1 << endl;
cout << "\ta2=" << a2 << endl;
return ;
} //不要在返回指向局部变量或者临时对象的引用。函数执行完毕之后,局部变量和临时对象将消失,引用将指向不存在的数据。

特别注意

参考:

我们发现,在C++中,有些成员函数返回的是对象,而有些函数返回的又是引用。

返回对象和返回引用的最主要的区别就是函数原型和函数头。

Car run(const Car &)     //返回对象

Car & run(const Car &)   //返回引用

        返回对象会涉及到生成返回对象的副本,这事调用函数的程序可以使用的副本,因此,返回对象的时间成本包括了调用复制构造函数来生成副本所需的时间和调用析构函数删除副本所需的时间。返回引用可以节省时间和内存。直接返回对象与按值传递对象类似,他们都生成临时副本。同样,返回引用与按引用传递对象类似,调用和被调用的函数对同一个对象进行操作。

        并不是总是可以返回引用的,当函数不能返回在函数中创建的临时对象的引用,因为当函数结束时,临时对象将消失,因此这种引用是非法的,在这种情况下,应返回对象,以生成一个调用程序可以使用的副本。
RMB& RMB::operator++()
{
yuan++;
return *this;
}
RMB RMB::operator++(int)
{
RMB temp(yuan); //创建对象
yuan++;
return temp; //返回对象的副本
}

         通用的规则是,如果函数返回在函数中创建的临时对象,则不要使用引用,如果先创建一个对象,然后返回改对象的副本,则可以使用返回对象,如上述第二种情况。

        如果函数返回的是通过引用或指针传递给它的对象,则应当按引用返回对象。当按引用返回调用函数的对象或作为参数传递给函数的对象。如上面的第一种情况。

二:

返回值是静态变量

#include <iostream>
using namespace std; // 变量是static 或者是全局变量 int j1()
{
static int a = ;
cout << &a << endl;
a++;
return a;
} int& j2()
{
static int a = ;
cout << &a << endl;
a++;
return a;
} // 若返回静态变量或全局变量
// 可以成为其他引用的初始值
// 即可作为左值,也可以作为右值使用
int main()
{
int a1 = j1();
int a11 = j1();
int a2 = j2();
int &a3 = j2();
// 因为是静态变量所以a3会是12
cout << "a1=" << a1 << "\ta11=" << a11<<endl;
cout << "a2=" << a2 << "\ta3=" << a3 << endl; return ;
}

返回静态变量

结果:

0x55ac0a859058
0x55ac0a859058
0x55ac0a85905c
0x55ac0a85905c
a1= a11=
a2= a3=
#include <iostream>
using namespace std; // 函数当左值
// 返回变量的值
int g1()
{
static int a = ;
a++;
return a;
}
// 返回变量本身(地址)
int& g2()
{
static int a = ;
a++;
cout << "a = " << a << endl;
return a;
}
int main()
{
// g1() = 100; // 就是 11 = 100所以错误
g2() = ; // 函数返回值是一个引用,并且当左值,a = 100
g2(); // int c = g2(); // 当右值 102
return ;
}

函数当左值使用,右值使用

当返回值是一个非基础类型,如一个类

就要涉及到拷贝赋值,运算符重载,还可能涉及深浅拷贝

静态变量使用方法:

https://www.cnblogs.com/33debug/p/7223869.html

常引用

const 引用

#include <iostream>
#include <cstdlib> using namespace std; // 常引用的知识架构
int main()
{
// 普通引用
int a = ;
int &b = a;
cout << " b= " << b << endl; // 常引用
int x = ;
const int &y = x; // 常引用,让变量拥有只读属性,不能通过y来修改x
// y++; 报错
cout << " y = " << y << endl; // 常引用 初始化 分为两种情况
// 1> 用变量初始化 常引用
{
int x1 = ;
const int &y1 = x1; // 用x1变量去初始化 常引用
} // 2>用常量初始化,常量引用
{
const int c = ; // c++编译器把c放到符号表中
// int &t = 10; // 错误 ,普通引用,引用一个字面值,因为这个字面值没有内存地址
// 引用 就是给内存取多个门牌号(多个别名)
const int &t = ; // c++编译器 会分配内存空间给t
} return ;
}

常引用和普通引用初始化

//思考:
//1、用变量对const引用初始化,const引用分配内存空间了吗?
//2、用常量对const引用初始化,const引用分配内存空间了吗?
//int &m = 10; //引用是内存空间的别名 字面量10没有内存空间 没有方法做引用
常引用总结
1)Const & int e(引用不能改变,引用所指对象也不能够改变)  相当于 const int * const e
2)普通引用 相当于 int *const e1
3)当使用常量(字面量)对const引用进行初始化时,C++编译器会为常量值分配空间,并将引用名作为这段空间的别名
4)使用字面量对const引用初始化后,将生成一个只读变量

const 的几种用法:

用const修饰函数参数

void test(const ClassA* a)
{
ClassA** b = &a; //编译错误,不能对const指针取地址
ClassA* c = a; //编译错误,不能将const指针转普通指针
(*b) = new ClassA();
}
void test2(ClassA* a)
{
ClassA** b = &a;
(*b) = new ClassA();
}

修饰指针,防止指针被修改

void test(const int a)
{
a++; //编译错误
int* c= &a; //编译错误,不能取地址,否则就具备了改a的能力
int b = a; //不用强转也可以编译通过,但还是没能力改a的值
}

修饰普通类型,防止被修改

修饰引用类型,参数的值不能被修改, 也就失去了引用类型的效果,但传递对象时,可以起到不copy对象的目的。

void test(const int& a)
{
a = ; //编译错误,不能修改const引用类型的值
}
void test(const ClassA& a) //传递的时候,不需要copy一个新的ClassA,又能保护a
{ }

二:

用const修饰局部或全局变量,功能类似函数参数

三:

用const修饰函数返回值,说明函数的返回类型是const的,功能类似于函数参数

const int test()
{
int a = ;
return a;
}

四:

用const修饰函数,说明函数不会修改成员变量的值

class ClassA
{
public:
int b;
int test() const
{
b = ; //编译错误,const修饰的函数不能修改类成员变量的值
return b;
}
}

c++ c的拓展的更多相关文章

  1. C++对C的函数拓展

    一,内联函数 1.内联函数的概念 C++中的const常量可以用来代替宏常数的定义,例如:用const int a = 10来替换# define a 10.那么C++中是否有什么解决方案来替代宏代码 ...

  2. RabbitMQ + PHP (二)AMQP拓展安装

    上篇说到了 RabbitMQ 的安装. 这次要在讲案例之前,需要安装PHP的AMQP扩展.不然可能会报以下两个错误. 1.Fatal error: Class 'AMQPConnection' not ...

  3. chrome拓展开发实战:页面脚本的拦截注入

    原文请访问个人博客:chrome拓展开发实战:页面脚本的拦截注入 目前公司产品的无线站点已经实现了业务平台组件化,所有业务组件的转场都是通过路由来完成,而各个模块是通过requirejs进行统一管理, ...

  4. 搭建LNAMP环境(七)- PHP7源码安装Memcached和Memcache拓展

    上一篇:搭建LNAMP环境(六)- PHP7源码安装MongoDB和MongoDB拓展 一.安装Memcached 1.yum安装libevent事件触发管理器 yum -y install libe ...

  5. jQuery的DOM操作实例(2)——拖拽效果&&拓展插件

    一.原生JavaScript编写拖拽效果 二.jQuery编写的拖拽效果 三.在jQuery中拓展一个拖拽插件

  6. 使用TypeScript拓展你自己的VS Code!

    0x00 前言 在前几天的美国纽约,微软举行了Connect(); //2015大会.通过这次大会,我们可以很高兴的看到微软的确变得更加开放也更加务实了.当然,会上放出了不少新产品和新功能,其中就包括 ...

  7. Unity、c#中的拓展方法讲解

    很早以前看过这个东西,但是没有真正的用到过,希望之后会用到上面的方法, 大概的意思是这样的c#中尤其在用Unity的时候,已有的框架提供给我们一些固定的方法,但是有时我们想对这些方法进行修改, 这时我 ...

  8. 关于java中自增,自减,和拓展运算符的小讨论

    java中运算符很多,但是能深入讨论的不算太多.这里我仅仅以++,*=为例做讨论. 例:++ i=0; i=i++ + ++i;//i=1 i=++i+i++;//i=2 i=i++ -++i;//i ...

  9. windows 平台 php_Imagick 拓展遇到的那些坑!

    我的php环境是使用了phpstudy 下载地址:http://www.phpstudy.net/a.php/211.html 最终并未解决问题 持续更新~ 1.首先到官网上 http://www.i ...

  10. linux下php-mysql拓展安装

    今天遇到一个奇怪的问题: 在服务器A上部署应用,在服务器B上部署数据库和缓存. 服务器A:apache2.2,php5.3 服务器B:mysql5.5,redis2.4 问题现象: 本地远程连接服务器 ...

随机推荐

  1. nwjs-简介

    nwjs是基于nodejs的,它支持nodejs所有的api,主要用于跨平台轻量级桌面应用开发,运行环境包括32位和64位的Window.Linux和Mac OS nwjs是在英特尔开源技术中心创建的 ...

  2. Linux学习笔记-第7天 - 编程还是要多写多练

    编程思路很重要,多写是要熟悉命令用法,多练不只是要熟悉语句常用在什么环境,更要在其基础上,尝试更多的写法.

  3. <DFS & BFS> 130 127

    130. Surrounded Regions BFS: 把所有边界上的O先换成A(标记),再深度遍历周围的点. 最后把O换成X(表示不符合要求),所有的A换回O class Solution { p ...

  4. 关于RAMOS所用的操作系统

    关于RAMOS所用的操作系统 RAMOS所用的操作系统,XP就不用说了,精简版最小的600MB到1.5GB的都有.现代意义上的WIN7/8/10 RAMOS一般选用精简版操作系统,节约内存的同时,还能 ...

  5. koa2+mysql+vue实现用户注册、登录、token验证

    说明: node.js提供接口,vue展现页面,前后端分离,出于编辑器功能和编辑习惯,vue用HbuilderX,node.js用VScode.(PS:仅作为学习笔记,如有不当之处欢迎指出,在此先谢为 ...

  6. MySQL实战45讲学习笔记:第三十讲

    一.复习一下加锁规则 在第20和21篇文章中,我和你介绍了 InnoDB 的间隙锁.next-key lock,以及加锁规则.在这两篇文章的评论区,出现了很多高质量的留言.我觉得通过分析这些问题,可以 ...

  7. 数论问题(1) : poj 1061

    最近,本人发现了一个新网站poj(不算新) 当然了,上面的资源很好...... 就是还没搞清楚它的搜索该怎么弄,如果有大佬能教教我怎么弄,请在下方留言 闲话少说,回归我们的正题 题目转自poj 106 ...

  8. [LeetCode] 92. Reverse Linked List II 倒置链表之二

    Reverse a linked list from position m to n. Do it in one-pass. Note: 1 ≤ m ≤ n ≤ length of list. Exa ...

  9. 用SQL语句去掉重复的记录

    删除手机(mobilePhone),电话(officePhone),邮件(email)同时都相同的数据 1.delete from 表 where id not in (select max(id) ...

  10. 在 C++ 中使用 QML 对象

    看过了如何在 QML 中使用 C++ 类型或对象,现在来看如何在 C++ 中使用 QML 对象. 我们可以使用 QML 对象的信号.槽,访问它们的属性,都没有问题,因为很多 QML 对象对应的类型,原 ...