C家家
来源:互联网 发布:华为手机淘宝网 编辑:程序博客网 时间:2024/04/30 02:04
第一课C到C++的升级
1:c++强调类型
C++强调类型,C不是很强调类型;
例如:在C++编译器中
struct student{
const char* name;
Int age;
};
这里相当于定义了一种新的student类型。可以直接用student去定义一些变量。
例如:student student1,student2;
但是在C编译器中
struct student{
const char* name;
int age;
};
这里只是相当于定义了一组变量的集合,必须要把这种变量的集合或者说标识符重新命名,才可以用来定义变量。
例如:typedef struct _tag_student student;
2:关于空形参问题
fun()与fun(void)之间的区别
在C++编译器中:这两者是相同的含义。都是表示返回值为int的无参函数。
所以:g() { return 5;}这样定义是错误的,因为C++是强调类型的。但是这样对于C编译器是认可的,因为C编译器是弱类型的,括弧内没有参数表示可以接收任意多个参数。
3:其它内容
register在C++能取地址;C++允许随处定义变量。
第二课C++的const分析
主要内容:
1:C++和C的const实现原理对比分析
2:const与宏定义之间的区别
1:C++和C的const实现原理对比分析
C语言中的const修饰的变量实际上还是一个变量,只是一个只读变量,是可以通过指针来改变,改变方法就是对这个const变量取地址,然后用指针指向它,去修改这个指针指向的内容,所以说C语言中const修饰的并不是真正意义上的常量,而是一个只读变量。但是有一种情况例外,就是C语言用const修饰的全局变量,编译器把它放在只读存储区里面。
C++里面的const修饰的变量,编译器对它的处理,是把这个放在符号表里面,符号表就是一个二维表,里面有符号和对应的值。假设采用与C语言类似的方法取地址,然后改变指针指向的内容的方法,编译器会重新分配一块空间,你的指针改变的内容并不是符号表里的值,而是另外开辟了一块空间的值。所以C++里面的const修饰的的确是常量,而不是只读变量。
C++里面对const常量使用了extern,也会分配存储空间。
举个例子:
例如:const int a = 2; const int b = 5;
在C编译器里面:int arr[a+b];这样编译器会报错,因为a和b实际上都是变量,两个变量怎么相加。只是一种特殊的变量。
在C++编译器里面:int arr[a+b];这样可以编译通过,因为这里C++编译器从符号表里面把这个两个东西拿出来相加,这里是真正的常量。
例如:
const int c = 0;
int* p = (int*)&c;
printf("Begin...\n");
*p = 5;
上面这个程序在C编译器里就会改变这个所谓常量的值。但在C++里却不会。
2:const与宏定义之间的区别
宏定义发生在预处理阶段,不会检查作用域和类型。这就是本质的区别。
void f()
{
#define a 3
const int b = 4;
}
void g()
{
printf("a = %d\n", a);
//printf("b = %d\n", b);
}
第三课布尔类型和引用
主要内容:
1:布尔类型的数据类型、数值运算、赋值问题、布尔类型用途
2:三目运算符的升级
3:C++中的引用
1:布尔类型的数据类型、数值运算、赋值问题、布尔类型用途
布尔类型是C++新增的一个数据类型。
理论上编译器给它分配一个字节。
取值只有两种:true和false。
数值运算:
bool b = 0;
b++; b = b - 3;这两项会把它作为一个字节来运算,运算结果分别是1和-3,都是非0。true.
赋值问题:
bool b = false; // 定义一个布尔类型,布尔类型名为b,值为false。
int a = b; // 这里将布尔类型赋值给一个整形。a的值为0。
b = 3; // b是一个布尔类型,这里表示true,而不是3。
a = b; // 这里将true赋给a,所以整形a的值为1。
b = -5; // 这个和上面的问题类似
a = b; // a的值为true,打印出来为1。
上面的几个赋值:总结出来就是右值为布尔类型,赋值给左边的不管什么类型,只有两种可能:不是1就是0。
a = 10; // 这个a打印出来肯定是10
b = a; // 这里打印出来是1,这个赋值没什么歧义。
2:三目运算符的升级
int a = 1,b = 2;
(a < b ? a : b) = 3;
上面这个三目运算符表达式,在C编译器中,可以等价为(1 < 2 ? 1 : 2) = 3;左值为一个常量,所以编译肯定会出错。
在C++里面对这个进行了升级,这里返回值为一个引用,相当于(&a < &b ? &a : &b) = 3;所以这里的左值为一个引用变量。
这里如果这样改一下:(a < b ? 1 : b) = 3;这样修改以后,C++编译器检查值发现里面居然有一个常量,常量没办法取引用,所以编译报错。引用的意思就是给变量取一个别名,就相当于一个人有小名和大名一样。
3:C++中的引用
引用的语法:Type &name = var;
普通引用定义时必须用同类型的变量进行初始化。
例如:int a = 5;
float& b = a; // 这样定义就不对了。两个类型都不同,一个是人,一个是老虎,怎么可能是一个。
第四课引用的本质分析
主要内容:
1:引用的赋值和概念
2:引用的本质
3:引用出错问题
1:引用的赋值和概念
1.1:概念
当引用作为函数的形参时,我们可以先不初始化,在调用函数的时候再初始化。
引用就是给变量起了一个别名,一般必须用变量来初始化引用。但也有例外,下面会说。
编译器会给引用变量分配一块内存空间。
1.2:用变量给引用赋值
给变量a起了一个别名:
int a = 8;
int& b = a; // 编译可通过
用字面常量赋值是不对的:
int& b = 10; // 编译不通过
普通引用是不能用字面常量来初始化的,只能用变量来初始化
用 const 修饰的引用叫常引用
常引用是允许用字面常量直接赋值
并且编译器会给这个引用分配4字节内存空间,该内存空间为只读:
const int& b = 10;
// b 为所分配的内存空间的别名
// &b 指向所分配的内存空间
常引用示例1:
常引用示例2:
常引用仍然可以像一般引用一样用变量赋值
int a;
int * p;
const int& b = a; // b 是a所代表的内存的别名,该内存变为只读
p = (int*)&b;
*p = 20; // 通过指针改变常引用b所代表的内存的内容
b = 88; // 报错,b为常引用
printf("b = %d\n",b); // 打印结果: 20
1.3:用字面常量给引用赋值
用 const 修饰的引用叫常引用
常引用是允许用字面常量直接赋值
并且编译器会给这个引用分配4字节内存空间,该内存空间为只读
例如:
int * p;
const int& b = 30; // 常引用可以用字面常量直接赋值。
p = (int*)&b;
*p = 20; // 通过指针改变常引用b所代表的内存的内容
b = 88; // 报错,b为常引用
printf("b = %d\n",b);
这就是和const直接声明不同,const是符号表的方式,不允许改变值的。
2:引用的本质
先看一段代码:
引用是变量的别名,内部实现为指针
引用的本质就是常量指针,即指针的值不能变
例如:
int& a; 等价于 int* const a;
void f(int& a) 等价于 void f(int* const a)
{ {
a = 5; *a = 5;
] }
3:引用出错问题
引用指向一个局部变量,例如:
int& demo()
{
int d = 0;
return d;
}
int main()
{
int& rd = demo(); //这个地方返回的是一个局部变量,
//但是这个局部变量在demo
//函数调用完以后,释放掉了,
//所以引用rd是一个释放掉的局部变量的别名。
}
上面这个程序可以将那个局部变量修改成static,让它不要释放。这样引用就可以指向一个真正的值了。
第五课构造函数上
主要内容:
1:引入构造函数的目的
2:构造函数的编写方法
1:引入构造函数的目的
构造函数就是对类成员变量进行初始化,但是如果不用构造函数能否初始化呢?或者说我不用构造函数,默认值是不是零呢?看下面几个例子:
首先定义一个类:
class Test
{
private:
int i;
int j;
public:
int getI() { return i; }
int getJ() { return j; }
};
1.1:在静态存储区创建对象,成员变量初始化值为0
静态存储区创建的对象实际上就是全局对象
因为在编译的时候,会把全局变量区域也就是静态存储区的所有变量全部初始化为0。
访问方法:gt.getI()和gt.getJ()。
1.2:在栈区创建对象,成员变量的初始值为随机数
在栈区创建对象就是定义一个局部变量。因为在栈区,没有经过编译器的初始化阶段,所以初始值肯定是一个随机数。是一个乱码。
1.3:在堆区创建对象,成员变量的初始值为随机数
分配和释放的方法,这里得到依旧是随机数。原因和栈区一样。
上面的说法是无法初始化,就是初始化也不可能有默认值。假设我在类里面定义一个初始化函数,然后在初始化之前就调用他,这种方法是否可行。下面做一个实验试试:
1.4:自己定义一个初始化函数来尝试初始化
调用初始化函数以后,要马上使用。不然就会出现随机值。
鉴于上面四种情况,引入了构造函数的概念。
2:构造函数的编写方法
构造函数的特点:
a:没有任何返回类型的声明。
b:构造函数在对象定义时候被自动调用。
c:名字和类名相同。
d:构造函数可以重载
e:构造函数不一定是public。
f:构造函数可以有return。
这个类里面的Test()函数就是一个构造函数。
第六课构造函数中
主要内容:
1:带参数的构造函数
2:赋值和初始化的区别
3:定义和声明的区别
4:构造函数的调用问题,可以手动调用
1:带参数的构造函数
就是构造函数里面有形式参数,例如
这就是构造函数的重载,这个没什么好说的。
2:赋值和初始化的区别
初始化意味着要调用构造函数,而赋值不会。看下面几个例子:
int i;这个i已经完成了初始化,如果是在静态变量区,会被编译器赋值为0,前面已经说过。但是它初始化的值是一个随机值。
i = 8; 这里执行的是赋值操作。不是初始化。
int i = 8;这种相当于初始化。
在C++中,看下面的例子:
Test t; // 调用 Test()
Test t1(1); // 调用 Test(int v)
Test t2 = 2; // 调用 Test(int v) 这几个会调用构造函数,因为属于初始化。
但是:
t = t2;这里直接把t2这个对象赋值给t这个对象,这样不会去调用构造函数,因为不是初始化。
3:定义和声明的区别
定义指的是申请对象并调用构造函数,例如Test t;
声明指的是告诉编译器存在这样一个对象,例如extern t;
定义声明最重要的区别:
定义创建了对象并为这个对象分配了内存,声明没有分配内存.
4:构造函数的调用问题,可以手动调用
构造函数自动调用不用说了,就是定义一个对象的时候自动去调用构造函数。但是假设要你定义一个数组对象,并且想要初始化好数组里的初值。看下面这个例子
Test ta[3];像这样定义了一个数组对象ta,有三个元素,但是初始值都为0。但是我想指定初始值,就只能手动去调用构造函数。调用方法如下:
Test ta[3] = {Test(), Test(1), Test(2)}; 这样就可以了。
在单独定义一个对象也可以手动调用,例如:Test t = Test(100);
第七课构造函数下
主要内容:
1:两个特殊的构造函数
2:拷贝构造函数的意义
1:两个特殊的构造函数
一个是无参构造函数
一个是拷贝构造函数
在没有手工定义任何构造函数的情况下,编译器会提供一个无参构造函数。这里说的是任何构造函数也包括拷贝构造函数。
2:拷贝构造函数的意义
C语言里一种很简单的赋值方式:
int i = 5; 这里完成变量i的定义和初始化。
int j;
j = i; 这里完成赋值了。
C语言可以赋值,那C++呢,肯定也可以赋值的。
例如:
Test t1;
Test t2 = t1; //这里如果类里面没有定义拷贝构造函数,那么编译器就会有一个自定义的拷贝构造函数,完成浅拷贝。浅拷贝的结果是物理状态相同。
也可以这样调用Test t2(t1);
物理状态的意思是:我一个对象里的值原来是什么值,拷贝给另一个对象以后,还是那个值。我一个对象里的指针指向哪块内存,那么另外一个对象的指向也指向那块内存。
在释放的时候,就会报错了。因为同一块内存被释放了两次。
而深拷贝考虑的是逻辑状态相同。它这里的意思是保证逻辑值相同,但不保证指针一定都是指向一块堆内存这种物理状态。但是保证指针指向的内存里存储的数据是一样的。
一般性的原则,定义拷贝构造函数就定义成深拷贝。
第八课初始化列表的使用
主要内容:
1:初始化列表的引入
2:初始化列表的一些问题
1:初始化列表的引入
上面这个例子的cl是一个const成员变量,不能出现在赋值符号左边。但是直接定义又会说const成员变量没有赋值。于是初始化列表就出场了。
还有下面这种情况:定义一个类,然后在另外一个类里去定义这个类的对象,要完成初始化,也要用到初始化列表。
2:初始化列表的一些问题
2.1:初始化顺序的问题
初始化的顺序与成员的声明顺序相同,与初始化列表中的位置无关。例如上面的这个实例,它的初始化顺序应该是m2、m3、m1。而不是列表中的m1、m2、m3。
2.2:初始化列表先与构造函数的函数体执行
这句话不用解释了吧。
2.3:类中的const成员
类中的const成员会被分配空间,分配空间好理解。
类中的const成员本质是只读变量,这句话的意思是说它是一个变量,没有定义到符号表。可以通过指针改变const变量的值。
类中的const成员只能通过初始化列表来完成初始化。这句话的意思是因为const是一个只读变量,所以不能出现在赋值符号左边。所以必须用初始化列表来完成初始化。
修改const只读变量的方法如下:
第九课对象的构造顺序
主要内容:
1:局部对象的构造顺序依赖于程序执行流,假设程序执行流突然goto了,或者其它问题到导致程序执行流被打断了。有可能出现对象没有产生,使用对象导致程序崩溃的问题。
2:堆对象的构造顺序依赖于new的使用顺序。
3:全局对象的构造顺序不确定,尽量的减少全局对象的相互依赖。
第十课对象的销毁
主要内容:
大概的内容就是析构函数的引入,就是用户可能不记得去调用释放,导致内存泄露,所以引入了析构函数。另外就是析构函数不能重载,因为析构函数没有函数参数。
第十一课神秘的临时对象
主要内容:
1:临时对象的引入
2:具体实践
1:临时对象的引入
临时对象是直接调用构造函数,就会产生一个临时对象。但是会马上消失。
2:具体实践
这里C++编译器不会生成临时对象,但是还是推荐使用Test t = 10;这种情况是绝对不会生成临时对象的。因为临时对象会降低效率,也是bug的根源。
- C家家
- 家家都有机器人
- 我的家家
- 夷邑虾酥磷瓜家家道揽短铝步也俟
- 兆家家用榨油机简介
- 在这里安家家了
- 中醫秘笈大公開---家家必備十良方
- 北京纪行之五:家家都有机器人
- 家家有个机器人 机器人正经历“PC式发展”
- 玩家家连连看 v2.5 怎么用
- 京城数家家乐福顾客人数减少
- 大家好,我是乐家家十字绣,请多多指教
- 19家家居企业抵制天猫“双11”
- 偶尔回到家,看到家家储蓄的牛粪
- 美国住宅型航空小镇:家家门前都停着飞机
- 黄梅时节家家雨,青草池塘处处蛙
- 我们乐家家十字绣网站怎么标题全部一样啊?
- 国内某家家教网站站长的创业自述,前人的失败总结
- 我的asp.net内容管理系统如何迁移到asp.net mvc(四)自定义URL路径
- java--两种servlet(servlet实现类)
- codeblocks的openGL环境配置
- 将ResultSet转为List
- 数据结构000
- C家家
- Unicode 和 UTF-8 有何区别?
- redhat6.5 和 Centos6.5 打开终端的快捷键
- thinkphp缓存
- Atitit.软件架构高扩展性and兼容性原理与概论实践attilax总结
- Git仓库完整迁移
- VS Code
- 深入分析 Linux 内核链表
- java多线程编程1