深度剖析C++第一部分

1、类是一种模型,这种模型可以创建出一个对应的实体。有了类不一定有对应的实体,但是一个实体必定属于某一个类。

2、类用于抽象的描述 一类事物所持有的属性和行为;对象是具体的事物,拥有所属类中描述的一切 属性和行为。

3、类一定都源于生活,两个对象实例不可能完全相同。

4、类之间的基本关系:继承和组合。

继承:从已有的父类细分出来的类和原有的类之间具有继承关系(is-a);继承的子类拥有父类的所有属性和方法

组合:一些类的存在必须依赖于其他类,组合的类在某一个局部上由其他的类 组成 ;组合关系是整体和局部的关系(生死共存亡,比如电脑类由CPU类、内存类的、显卡类等组成)

5、一个类通常分为以下两个部分:类的实现细节(类的实现)和类的使用方式(类的声明);c++中的类支持声明和实现的分离;将类的实现和定义分开。.h头文件只有成员变量和成员函数的声明,.cpp文件中完成类的实现。

必须在类的表示方法中定义属性和行为的公开级别,类似文件系统中文件的权限。需要注意的一点是访问权限public和private都是针对类外部的访问而言的,在类内部无论是public还是private都是可以访问成员变量和成员函数的,即类成员的作用域与访问级别没有关系。

6、c++中struct和class的主要区别就是成员变量和成员函数的默认访问级别不同,struct的默认访问级别是public的,而class是private的。

7、从程序设计的角度来看,对象也只是个变量,他的类型是类 类型,因此在栈上创建对象时,成员变量初始值为随机值(eg:Test t);在堆上创建 对象时,成员变量初始值为随机值(eg:Test t =Test()  或者是Test *t =new Test(),注意这两种的区别,前者是一个临时对象的拷贝(构造函数本质上就是一个临时对象),后者是类对象指针的定义);在静态存储区创建对象时成员变量初始值为0(eg:全局变量)。

8、构造函数在对象创建后给对象的状态进行初始化,而初始化列表先于构造函数执行,是在对象创建时对对象进行初始化。

9、构造函数没有返回值,但可以有参数,即可以重载;而析构函数没有返回值也没有参数。

经典代码示例:

  1. #include <stdio.h>
  2. #include <iostream>
  3. class Test
  4. {
  5. private:
  6. int i;
  7. int j;
  8. public:
  9. Test()
  10. {
  11. i=0;
  12. j=0;
  13. }
  14. Test(int v1,int v2)
  15. {
  16. i=v1;
  17. j=v2;
  18. }
  19. ~Test()
  20. {
  21. printf("我是析构函数\n");
  22. }
  23. void sleep();
  24. void setValue(int m,int n);
  25. int getValue_i();
  26. int getValue_j();
  27. void eat();
  28. };
  29. void Test::sleep()
  30. {
  31. printf("我是睡觉动作\n");
  32. }
  1. void Test::eat()
  2. {
  3. printf("我是吃饭动作\n");
  4. }
  5. void Test::setValue(int m,int n)
  6. {
  7. i=m;
  8. j=n;
  9. }
  10. int Test::getValue_i()
  11. {
  12. return i;
  13. }
  14. int Test::getValue_j()
  15. {
  16. return j;
  17. }
  1. int main()
  2. {
  3. // ①Test t=Test(1,2);
  4. // ②Test t;
  5. // ③Test t(1,2);
  6. Test *t=new Test();//④
  7. t->setValue(1,2);
  8. printf("i=%d\n",t->getValue_i());
  9. printf("j=%d\n",t->getValue_j());
  10. t->~Test();//调用析构函数
  11. system("pause");
  12. return 0;
  13. }

10、对于构造函数,一个类中可以存在多个重载的构造函数(参数及其类型可以不同),构造函数的重载遵循C++重载的规则。注意:对象的定义和对象的声明不一样,对象的定义是指申请一个对应对象的空间并且调用构造函数;而对象声明是告诉编译器存在这样一个对象 。对象定义时即会触发 构造函数的调用,在一些情况下可以手动调用构造函数。

11、两个特殊的构造函数:(1)无参构造函数(2)拷贝构造函数(该种构造函数在创建对象时拷贝对象的状态

(1)无参构造函数就是没有参数的构造函数,函数体为空 Test(){}

(2)拷贝构造函数就是参数为(const class_name &obj)的构造函数;当类中没有定义拷贝构造函数时,编译器会默认提供一个拷贝构造函数,简单的进行成员变量的值复制浅拷贝) 。   【 编译器提供的就是浅拷贝。】

拷贝函数的意义:兼容C语言的初始化方式 ,另外初始化行为能够符合预期的逻辑。

拷贝构造函数包含浅拷贝(拷贝后对象的物理状态相同)和深拷贝(拷贝后对象的逻辑状态相同)两种;有时候在资源分配上浅拷贝会带来一些问题,比如在类中我随便定义一个指针,这个指针指向的内存地址为0x1234;当使用浅拷贝时候(我们把一个对象复制给另一个对象),拷贝后的那个对象中的指针也同样会指向内存地址0x1234;那么我们在释放指针空间的时候只能释放一次,

这就是浅拷贝物理意义上的拷贝;而 如果是深拷贝的话我们在把一个对象的指针复制给另一个对象的时候,会在内存空间重新找一块区域,把数值放进去,在释放指针的时候也可以两次释放,这就是逻辑状态下的深拷贝。

(3)什么时候需要深拷贝?一般情况下对象中有成员指代了系统中的资源(包括动态内存空间、指针定义、文件打开、网络端口的使用等等)时候需要深拷贝,当我们在类中自定义了拷贝构造函数时,必然要实现深拷贝(否则就使用编译器提供的浅拷贝了)。

代码示例:

自定义浅拷贝构造函数

  1. #include <stdio.h>
  2. #include <iostream>
  3. class Test
  4. {
  5. private:
  6. int i;
  7. int j;
  8. public:
  9. Test()
  10. {
  11. i=1;
  12. j=2;
  13. }
  14. Test(const Test &obj)
  15. {
  16. i=obj.i;
  17. j=obj.j;
  18. printf("我是显示调用的构造函数\n");
  19. }
  20. int getValue()
  21. {
  22. return i+j;
  23. }
  24. };
  25. int main()
  26. {
  27. Test t1;
  28. Test t2(t1);//Test t2=t1;兼容C语言的复制
  29. printf("value =%d\n",t2.getValue());
  30. system("pause");
  31. return 0;
  32. }

                                         自定义深度拷贝构造函数

  1. #include <stdio.h>
  2. #include <iostream>
  3. /*
  4. #include "DataStruct.h"
  5. #include <array>
  6. */
  7. class Test
  8. {
  9. private:
  10. int i;
  11. int j;
  12. public:
  13. int *p;
  14. public:
  15. Test()
  16. {
  17. i=1;
  18. j=2;
  19. p=new int; //重要
  20. *p=10;
  21. }
  22. Test(const Test &obj)
  23. {
  24. i=obj.i;
  25. j=obj.j;
  26. p=new int; //重要
  27. *p=*obj.p; //逻辑拷贝 只把值拷贝过来 p的地址可以与t1的不同
  28. printf("拷贝后p的地址为:%p\n",p);
  29. printf("我是显示调用的构造函数\n");
  30. }
  31. int getValue()
  32. {
  33. return i+j;
  34. }
  35. void free()
  36. {
  37. delete(p);
  38. }
  39. };
  40. int main()
  41. {
  42. Test t1;
  43. Test t2(t1);//Test t2=t1;兼容C语言的复制
  44. printf("t1.p=%p\n",t1.p);
  45. printf("t2.p=%p\n",t2.p);
  46. printf("value =%d\n",t2.getValue());
  47. t1.free(); //深拷贝时实现两侧free释放
  48. t2.free();
  49. system("pause");
  50. return 0;
  51. }

12、类中是可以定义const成员的,c++中提供了初始化列表对成员变量进行初始化,语法规则:
ClassName::ClassName():m1(v1),m2(v2),m3(v3){

//其他的一些初始化操作

}

类中的const成员是会被分配空间的,它的本质是只读变量,且类中的const成员只能在初始化列表中指定初始值。编译器无法直接得到const成员的初始值,因此无法进入符号表成为真正意义上的常量。const成员变量为只读变量。const成员变量必须 在初始化列表中指定初值。初始化列表先于构造函数体执行。类中可以使用初始化列表对成员进行初始化。初始化列表的初始化顺序与成员变量的声明顺序有关,与初始化列表的顺序位置无关。

初始化列表代码示例:

  1. #include <stdio.h>
  2. class Value
  3. {
  4. private:
  5. int mi;
  6. public:
  7. Value(int i)
  8. {
  9. printf("i = %d\n", i);
  10. mi = i;
  11. }
  12. int getI()
  13. {
  14. return mi;
  15. }
  16. };
  17. class Test
  18. {
  19. private:
  20. Value m2;
  21. Value m3;
  22. Value m1;
  23. public:
  24. Test() : m1(1), m2(2), m3(3)
  25. {
  26. printf("Test::Test()\n");
  27. }
  28. };
  29. int main()
  30. {
  31. Test t;
  32. return 0;
  33. }

13、类中多个对象的构造顺序:对于局部对象,当程序执行流到达对象的定义语句时进行构造。对于堆对象,当程序流到达new语句时创建对象,使用new创建对象将自动触发构造函数的调用;对于全局对象,对象的构造顺序是不确定的,不同的编译器使用不同的规则来确定构造顺序。

14、析构函数的定义准则:当类中自定义了构造函数,且构造函数中使用了系统资源(指针、内存操作、文件等)则需要自定义析构函数;析构函数没有返回值也没有参数。析构函数是对象销毁时进行清理的特殊函数,析构函数在对象销毁时被自动调用,他是对象释放系统资源的保障。

15、直接调用构造函数会产生一个临时对象,临时对象的生命周期只有执行一条语句的时间,临时对象的作用域只在一条语句中。实际开发中要人为的避开临时对象。

16、一般情况下都需要显式自定义构造函数和析构函数 ,总结:构造函数必定义;析构函数必定义。拷贝构造函数根据系统资源是否用到定义,用到后需要深拷贝自定义构造函数;

17、析构函数和构造函数都是被对象调用的,不包括对象指针;临时对象也会调用构造函数和析构函数。还有就是析构函数是释放对象的,包含临时对象,如果想释放对象指针或其他指针,则需要在析构函数中显示定义,相当于free函数,对象指针以及其它类型的指针需要使用delete释放。

  1. #include <stdio.h>
  2. class Test
  3. {
  4. int mi;
  5. public:
  6. Test(int i)
  7. {
  8. mi = i;
  9. printf("Test(): %d\n", mi);
  10. }
  11. ~Test()
  12. {
  13. printf("~Test(): %d\n", mi);
  14. }
  15. };
  16. int main()
  17. {
  18. Test t(1);
  19. Test* pt = new Test(2);
  20. delete pt;
  21. return 0;
  22. }

//输出结果1 2 2 1

经典代码示例(一):开发一个 数组类解决 原生数组 的安全性问题。

经典代码示例(二):开发一个四则运算。

补充:

C++中有三种创建对象的方法

  1. #include <iostream>
  2. using namespace std;
  3. class A
  4. {
  5. private:
  6. int n;
  7. public:
  8. A(int m):n(m)
  9. { }
  10. ~A(){}
  11. };
  12. int main()
  13. {
  14. A a(1);  //栈中分配
  15. A b = A(1);  //栈中分配   或者直接 A b = 1;
  16. A* c = new A(1);  //堆中分配
  17.   delete c;
  18. return 0;
  19. }
  20. 第一种和第二种没什么区别,一个隐式调用,一个显式调用,两者都是在进程虚拟地址空间中的栈中分配内存,而第三种使用了new,在堆中分配了内存,而栈中内存的分配和释放是由系统管理,而堆中内存的分配和释放必须由程序员手动释放。采用第三种方式时,必须注意一下几点问题:
    1. new创建类对象需要指针接收,一处初始化,多处使用
    2. new创建类对象使用完需delete销毁
    3. new创建对象直接使用堆空间,而局部不用new定义类对象则使用栈空间
    4. new对象指针用途广泛,比如作为函数返回值、函数参数等
    5. 频繁调用场合并不适合new,就像new申请和释放内存一样
    6. 栈的大小远小于堆的大
    7. 栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率 比较高。堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在 堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会 分 到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。

(3)狄泰软件学院C++课程学习剖析一的更多相关文章

  1. (3.3)狄泰软件学院C++课程学习剖析四

    对课程前面40课的详细回顾分析(二) 1.一个类的成员变量是对于每个对象专有的,但是成员函数是共享的. 2.构造函数只是决定一个对象的初始化状态,而不能决定对象的诞生.二阶构造人为的将初始化过程分为了 ...

  2. (3.2)狄泰软件学院C++课程学习剖析三

    对课程前面40课的详细回顾分析(一) 0. int main() { // ① Array t(3,3); //普通模式 // ② Array *t=new Array(3,3); //指针方式 // ...

  3. (3.1)狄泰软件学院C++课程学习剖析二

    深度剖析C++第二部分 1.通过对象名能够访问public成员变量.每个对象的成员变量都是专属的,成员变量不能够在对象之间共享. 2.需求:统计在程序运行期间某个类的对象数目,保证程序的安全性(不能使 ...

  4. 王之泰《面向对象程序设计(java)》课程学习总结

    第一部分:理论知识学习部分 总复习纲要 1. Java语言特点与开发环境配置(第1章.第2章) 2. Java基本程序结构(第3章) 3. Java面向对象程序结构(第4章.第5章.第6章) 4. 类 ...

  5. C语言课程学习的总结

    C语言课程学习的总结 学习C程序这门课一年了,这是我们学的第一门专业课.在大学里,C语言不但是计算机专业的必修课程而且也是非计算机专业学习计算机基础的一门必修课程.所以作为我这个计算机专业的学生来说当 ...

  6. solr课程学习系列-solr服务器配置(2)

    本文是solr课程学习系列的第2个课程,对solr基础知识不是很了解的请查看solr课程学习系列-solr的概念与结构(1) 本文以windows的solr6服务器搭建为例. 一.solr的工作环境: ...

  7. ACM课程学习总结

    ACM课程学习总结报告 通过一个学期的ACM课程的学习,我学习了到了许多算法方面的知识,感受到了算法知识的精彩与博大,以及算法在解决问题时的巨大作用.此篇ACM课程学习总结报告将从以下方面展开: 学习 ...

  8. node+vue进阶【课程学习系统项目实战详细讲解】打通前后端全栈开发(1):创建项目,完成登录功能

    第一章 建议学习时间8小时·分两次学习      总项目预计10章 学习方式:详细阅读,并手动实现相关代码(如果没有node和vue基础,请学习前面的vue和node基础博客[共10章]) 视频教程地 ...

  9. "做中学"之“极客时间”课程学习指导

    目录 "做中学"之"极客时间"课程学习指导 所有课程都可以选的课程 Java程序设计 移动平台开发 网络攻防实践 信息安全系统设计基础 信息安全专业导论 极客时 ...

随机推荐

  1. MongoDB优化心得分享

    这里总结下这段时间使用mongo的心得,列出了几个需要注意的地方. 1. 系统参数及mongo参数设置 mongo参数主要是storageEngine和directoryperdb,这两个参数一开始不 ...

  2. 无法打开内核设备"\\.\Global\vmx86":系统找不到指定的文件. 是否在安装 VMwareWorksation 后重新引到 ? 问题解决

    节前正常使用的工作环境, 过完春节后, 上班第一天就不正常工作了, 难不成机器也要放假休息, 虚拟机打不开了, 没办法办公可是不行的. 上网查原因, 解决问题. 上网看了很多关于此问题的解决办法, 很 ...

  3. 树莓派上编译安装python3.6

    1.更新树莓派系统 sudo apt-get update sudo apt-get upgrade -y 2.安装python依赖环境 sudo apt-get install build-esse ...

  4. Linux查看当前目录下所有文件中包含map的行记录

    find yaochi_e.prm |xargs grep -ri "map" grep -n "map" *.prm|grep -v "\-\-ma ...

  5. C# 中File和FileStream的用法

    原文:https://blog.csdn.net/qq_41209575/article/details/89178020 1.首先先介绍File类和FileStream文件流 1.1  File类, ...

  6. IMAP协议学习笔记(一)

    IMAP IMAP(Internet Mail Access Protocol,Internet邮件访问协议)以前称作交互邮件访问协议(Interactive Mail Access Protocol ...

  7. vue项目 PC端点击查看大图

    今天,发现了一款还不错的插件来实现查看大图,成熟度也比较高,支持各种操作 原作品的github地址为 https://github.com/mirari/v-viewer 也有对应的中文文档,使用方法 ...

  8. stylus快速上手

    定义变量,比如一键切换主题色 1.创建xxx.styl文件,定义变量 $bgColor = #00bcg4 2.在其他页面的style区域里,先引入这个xxx.styl文件 <style> ...

  9. 国内下载Flutter

    出现镜像错误等现象属于源码包不全面,国内下载镜像添加环境变量. ①:Linux执行如下操作 export PUB_HOSTED_URL=https://pub.flutter-io.cn export ...

  10. FCKeditor用在JSP中的几点注意事项

    转自:https://blog.csdn.net/asinzy/article/details/3854127 本篇文章主要介绍了"FCKeditor用在JSP中的几点注意事项", ...