C家家

来源:互联网 发布:华为手机淘宝网 编辑:程序博客网 时间:2024/04/30 02:04

CC++的升级

 

1c++强调类型

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:其它内容

registerC++能取地址;C++允许随处定义变量。


C++const分析

主要内容:

1C++Cconst实现原理对比分析

2const与宏定义之间的区别

1C++Cconst实现原理对比分析

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];这样编译器会报错,因为ab实际上都是变量,两个变量怎么相加。只是一种特殊的变量。

C++编译器里面:int  arr[a+b];这样可以编译通过,因为这里C++编译器从符号表里面把这个两个东西拿出来相加,这里是真正的常量。

例如:

const int c = 0;

int* p = (int*)&c;

printf("Begin...\n");

*p = 5;

上面这个程序在C编译器里就会改变这个所谓常量的值。但在C++里却不会。

2const与宏定义之间的区别

宏定义发生在预处理阶段,不会检查作用域和类型。这就是本质的区别。

void f()

{

    #define a 3

    const int b = 4;

}

void g()

{

    printf("a = %d\n", a);

    //printf("b = %d\n", b);

}


课布尔类型和引用

主要内容:

1:布尔类型的数据类型、数值运算、赋值问题、布尔类型用途

2:三目运算符的升级

3C++中的引用

 

1布尔类型的数据类型、数值运算、赋值问题、布尔类型用途

布尔类型是C++新增的一个数据类型。

理论上编译器给它分配一个字节。

取值只有两种:truefalse

 

数值运算:

bool  b  =  0;

b++;  b = b - 3;这两项会把它作为一个字节来运算,运算结果分别是1-3,都是非0true.

 

赋值问题:

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++编译器检查值发现里面居然有一个常量,常量没办法取引用,所以编译报错。引用的意思就是给变量取一个别名,就相当于一个人有小名和大名一样。

3C++中的引用

引用的语法: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:初始化顺序的问题

初始化的顺序与成员的声明顺序相同,与初始化列表中的位置无关。例如上面的这个实例,它的初始化顺序应该是m2m3m1。而不是列表中的m1m2m3

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的根源。











0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 苹果6螺丝拧花了怎么办 苹果电脑螺丝拧花了怎么办 苹果螺丝滑牙了怎么办 苹果7螺丝滑丝了怎么办 外六角螺丝滑牙怎么办 六角螺帽滑丝了怎么办 内六角螺丝滑头了怎么办 内六角螺丝滑失了怎么办 内六角螺钉滑了怎么办 三视图看不出来怎么办 小猫断奶以后母猫涨奶怎么办 手机螺丝滑丝了怎么办 螺丝孔道滑丝了怎么办 螺丝生锈了拧不下来怎么办 钣金加工六角网孔变形怎么办 外六角螺帽滑丝怎么办 内六角螺丝螺帽滑丝怎么办 一字螺丝钉脱扣拧不下来怎么办 一字螺丝拧花了怎么办 小螺丝卸不下来怎么办 机油螺丝滑丝了怎么办 刚滑双板膝盖滑的疼怎么办 lv包真皮弄脏了怎么办 lv包压变形了怎么办 lv的包包被压了怎么办 固态硬盘太小了怎么办 联想笔记本网络连接不可用怎么办 联想g50玩dnf卡怎么办 手机有wifi电脑没有网怎么办 电脑网卡被禁用了怎么办 win8系统装win7蓝屏怎么办 联想笔记本装win7蓝屏怎么办 联想g40-70开机黑屏怎么办 新主机开不了机怎么办 联想720s笔记本闪屏怎么办 华硕k40ie显卡坏了怎么办 开机黑屏进入bois后怎么办 2根内存条不兼容怎么办 联想笔记本r720系统崩溃怎么办 联想天逸310卡怎么办 新买的鼠标没反应怎么办