C/C++知识点总结(2)

C++引入类机制的目的:

  • 从语法上将数据和操作捆绑在一起;

  • 从语法上消除变量和函数的名字冲突;

  • 从语法上允许服务端设计者控制数据和函数的访问权限;

  • 从工程上支持数据封装、信息隐藏、将责任推向服务端、减小信息共享、独立问题域,减少信息的交换量,减少程序员之间的协调;

C++和C定义结构的区别:

  • C++中struct和class除了前者的缺省访问限制符为public,后者为private,继承关系中前者缺省为public,后者缺省为private之外,其他都相同;

  • C中的struct不能定义方法,表示一个纯数据结构,不能有嵌套函数,只能定义变量,但可以定义函数指针变量,从而具有模拟C++类方法的能力;

C++中常见类型的声明:(注意:[]的优先级大于*)

  • 整型数:int a;

  • 指向整型数的指针:int *a;

  • 指向一个指向整形数的指针的指针:int **a;

  • 数组元素为10个整型数的数组:int a[10];

  • 数组元素为10个指向整形数的指针的数组:int *a[10];

  • 指向一个由10个整型数组成的数组的指针:int (*a)[10];

  • 指向一个有一个整型数参数,返回值为一个整型数的函数的指针:

    int
    (*a)(int );


  • 数组元素为10个上述函数指针类型的数组:int (*a[10])(int );

C++空类(没有定义任何变量和函数)中,默认产生的六种类成员函数:


 1 class Empty
2 {
3 public:
4 Empty(); // 无参构造函数
5 ~Empty(); // 析构函数
6 Empty( const Empty& ); // 拷贝构造函数
7 Empty& operator=( const Empty& ); // 赋值运算符
8 Empty* operator&(); // 取址运算符
9 const Empty* operator&() const; // 常量取址运算符const
10 };

  • 这样的对象在内存中有一个字节(1byte)的占位符,如果有虚拟指针,则另外增加4个字节的内存耗用,一共5个字节;

  • 注意到有两个缺省的取值运算符,这样的设计时为了应对常量对象的声明;所以类定义中如果定义int getX();则应该定义对应的int getX()
    const重载函数(缺省类参数为const *this),以防止当申明常量对象时调用int getX()出现错误;

缺省构造函数(Default Constructor):

  • 如果类定义中没有显示定义任何构造函数,则编译器会自动为我们生成一个缺省构造函数,也就是无参构造函数;但是,一旦我们定义任何类型的构造函数后,编译器不会自动生成缺省构造函数;

    Cylinder(){……}

    Cylinder c; Cylinder c[100]; Matrix {Cylinder c; ……};


  • 如果我们在某类定义中定义了非无参构造函数,当此类型变量作为另一个类d的类成员,创建d类型的时候需要首先初始化其类成员,此时c需要一个无参构造函
    数,但此时编译器不再自动生成,所以会出现错误;另外一种错误情况就是定义Cylinder
    c[100];的时候;所以好的类设计总会提供一个无参构造函数;

  • Cylinder
    cl;会隐式调用缺省(无参)构造函数;但是调用malloc不会隐式调用任何构造函数;仅仅是在堆内存申请一块内存,并且也是C++中唯一可以不调用构造函数而创建对象的方法;

    Cylinder *c2=(Cylinder*)malloc(sizeof(Cylinder));

    良好的构造函数设计:

    class Point {…… Point(int a=0, int b=0):size(a),height(b) {……}};


  • 上述类定义中的构造函数可实现四个功能:

    如果创建对象时用户不传任何参数Point p;则可作为缺省无参构造函数;

    如果创建对象时用户仅提供一个参数Point p=20;Point p(10);则作为转换构造函数;

    如果创建对象时用户提供两个参数Point p(10,28);则作为一般构造函数;

    如果成员变量为对象,则成员初始化列表可以让成员仅初始化一次,提升程序性能;

拷贝构造函数(Copy Constructor):

  • 无论用户是否定义了其他的构造函数,编译器都会自动生成一个拷贝构造函数,除非用户显示定义自己的拷贝构造函数;

  • 但系统提供的拷贝构造函数在初始化拷贝时仅仅是将数据成员进行值复制(也就是浅复制),如果实例对象中有索引动态内存的指针变量,则拷贝前后会共享同一动态内存,此时需要特别注意动态内存的关系(这种情况下一般都需要重新定义拷贝构造函数)

    Cylinder(const Cylinder & copy) {……}

    Cylinder c1(1,3); Cylinder c2=c1; Cylinder c3(c1);


  • 如果定义为按值传递,则会造成调用结构上的无限递归;函数调用过程中编译器会使用实际参数值初始化形式参数,也就是调用拷贝构造函数,所以最终导致无限递归调用;

  • 另外注意区分拷贝构造函数和赋值操作符重载函数,前者在对象初始化的时候调用,后者在对象初始化之后调用,分别调用不同的函数;

    Cylinder c1(1,3); Cylinder c2=c1; Cylinder c3; c3=c2;

转换构造函数(Conversion Constructor):

  • 需要用户显式定义,编译器不会自动提供;此类构造函数有且只能有一个参数(特别像一般的构造函数),并且为非本类类型,则在调用函数的时候可写作,Object
    o(30);也可写作:Object c=30;

    Cylinder(int n) {……};

    首先调用转换构造函数利用30生成一个临时的匿名Object对象,然后使用拷贝构造函数或者赋值操作符重载函数进行与c的赋值操作;

    Cylinder c1=20; Cylinder c2; c2=10;

    所以使用内置基本类型对用户自定义类型进行初始化或者赋值时,编译器会首先检查这个自定义类型是否具有转换构造函数,然后再判断是否是语法错误;


  • 如果某个函数的形式参数为Object类型,但仅提供一个int类型的实际参数,此时如果Object类定义中定义了针对int类型的转换构造函数,则转换过程如下:

    1). 编译器先用转换构造函数将int类型的值生成一个临时的匿名Object对象;

    2).
    如果是其他数值类型(如double),编译器还会首先进行位提升或者类型强制转化(double->int,就是针对其他类型和转换构造函数的参数类型),之后再调用转换构造函数生成一个匿名的临时Object对象;

    3). 最后将这个临时的Object对象作为真正的参数传入函数;
    Cylinder(int n) {……};
    void
    Copy(Cylinder *to, Cylinder &from) {……}
    Cylinder c1;
    Copy(&c1,70.8);
    注意参数不能是指针类型,因为转换构造函数创建的是一个变量,而不是指针;

析构函数(Destruction Constructor):

  • 用于释放实例类在运行过程中申请的系统资源,比如堆内存;析构函数没有参数,没有返回值,有且仅有一个;如果没有显示定义则编译器会提供一个什么也不做的
    缺省析构函数;析构函数调用的顺序需要和构造函数调用的顺序相反,比如类成员中的对象需要优先调用自己的析构函数,继承关系中也需要让基类对象优先调用析
    构函数;

  • 调用析构函数的情景,其作用是在对象的内存空间被撤销之前做一些清理工作;

    1).
    对于extern和static变量,在main函数结束之后,程序结束之前;2). 对于auto变量,在作用域结束之前;

    3).
    对于heap变量,new/delete类型的话在delete的时候;malloc/free类型的话在free函数调用之前需要手动调用析构函数;

虚函数设计中需注意的问题:

  • 虚拟函数利用多态机制基于同一调用接口实现不同的功能;

  • 如果子类定义一个与父类中虚函数仅仅名字相同的函数(参数不同,返回类型不同),则不能产生多态机制,而仅仅是函数覆盖;(也就是说虚函数必须要求函数名和参数都相同,并且基类的函数标注virtual);

  • 虚函数实现依赖vtable,而vtable与实例对象绑定(this指针),所以仅有类成员函数才可声明为虚函数;全局函数,构造函数,static和inline修饰的函数则不能:

    static函数独立于实例对象;

    inline函数在编译时期就将函数声明替换为方法体;

    constructor函数调用的时候并不存在完整的对象;当调用构造函数的时候,这个对象的动态类型还不完整,没有办法确定它到底是什么类型,故构造函数不能动态绑定,不能是虚函数;而析构函数可以是虚函数;


  • 如果不能保证某个类不会有子类,一般需要将destructor函数定义为虚函数;

  • vtable在编译期就已经确定并与类定义绑定,实例对象则含有指向VT的指针;

Const关键字:

  • 修饰变量:此变量的值在定义时必须初始化,之后不可再赋值;甚至不可将其地址赋值给其他指针变量,仅当指针变量和引用变量前使用const时才可,此指针和引用变量不可用作左值;

  • 用于一般变量申明:表示常量变量,仅能在声明的时候赋值;

  • 用于参数申明:表示参数作为输入值,否则参数作为输出值;

  • 用于函数的返回值:表示返回值不能用于左值(不能改变);

  • 修饰函数:在函数名之后,{}之前添加const表示函数内部不会修改参数的值;如果此函数为类成员函数则说明不会修改所有成员变量的值;

  • 用于一般类成员函数申明(添加于函数末尾):表示此函数不修改此类的成员变量(const this的指针)

    class Cylinder {…… void show() const {……}};

    Point* colsestOne(const Point &p) {return …… this :

    const_cast<Point*>(&p);}

    使用const_cast<Type>(ConstType)可以将同类型的由const修饰的变量转换成没有const属性的变量,但仅去除常量属性仍旧不能修改变量的值;

各种程序设计机制存在的用意:

  • 用static修饰的类成员函数:此函数与类实例对象相互独立,所以完全可为全局函数,但为了从语法层面表明与某个类别相关,则在类定义内部实现,方便传达程序员的意图;可以直接使用类名调用,也可以使用对象调用;

    Point::samePoint(p1, p2);


  • 用const修饰的类成员函数:此函数不改变类对象成员的成员变量(全局函数则不改变参数变量),传入const
    *this指针;使用const修饰传入参数,则表示此参数为输入值,不可改变,否则为输出值,可以改变;

    void show() const {……}


  • 用Rational(long n=0, long
    d=1);定义构造函数:这个构造函数可以作为一般构造函数,转换构造函数,缺省构造函数,减少代码量;转换构造函数的调用通常为隐式行为,如果我们要求
    类型转换需要显式执行,则在这个构造函数函数前添加explicit,则仅当使用强制转换操作时才能调用此函数,不能隐式转换;

  • 用friend Rational operator+(const Rational &x, const Rational
    &y)定义友元函数:具有和类成员函数一样的访问权限,但编译器不为其缺省加入this指针,一般为全局函数或者另一个类定义的函数。用于解决重
    载运算符函数中,左操作数为数值类型,右操作数为对象类型的情况(一般的类的重载运算符函数中左操作数必须为对象本身,也就是缺省的this),当左操作
    数为数值类型时,首先调用类型转换函数将其变成类类型,然后调用对应的类重载操作符函数;

  • 使用成员初始化列表:在实现构造函数时在构造函数头和函数体之间加入定义,有4个方面的优势:使用成员初始化列表:在实现构造函数时在构造函数头和函数体之间加入定义,有4个方面的优势:

    可以避免类成员变量为对象变量时进行的不必要的缺省构造函数的调用(初始化列表并没有规定变量的初始化顺序,变量在类定义中的顺序决定其初始化顺序),提升性能;

    可以避免因为类变量定义中没有定义缺省构造函数而出现语法错误;

    可以避免const变量的错误初始化,类成员中的const变量由于只能在定义时初始化,所以需要使用初始化列表进行初始化;

    可以避免引用类型变量的错误初始化,类成员中的引用类型变量也只能在定义时初始化,所以需要使用初始化列表进行初始化;


  • 类定义中总是提供缺省构造函数:防止类型以无参数方式创建时发生错误(数组元素或者类成员变量);

  • 将拷贝构造函数声明为private:禁止按值传递此类对象;也可将其他对象的函数声明为private,达到对应的限制;

时间: 2024-08-27 16:14:56

C/C++知识点总结(2)的相关文章

H5移动端知识点总结

移动开发基本知识点 一. 使用rem作为单位html { font-size: 100px; }@media(min-width: 320px) { html { font-size: 100px; } }@media(min-width: 360px) { html { font-size: 112.5px; } }@media(min-width: 400px) { html { font-size: 125px; } }@media(min-width: 640px) { html { f

Spring知识点回顾(01)

Spring知识点回顾(01) 一.依赖注入 1.声明Bean的注解 @Component @Service @Repository @Controller 2.注入Bean的注解 @Autowired @Inject @Resource 二.加载Bean 1.xml方式 - applicationcontext.xml : Beans, Bean, Component-Scan 2.注解方式 - @Configuration,@ComponentScan,@Bean 用@Configurati

Javascript易错知识点

? JS易错知识点总结: == 和 === 的区别: ==:判断两个变量的值是否相等. ===:判断两个变量的类型和值是否都相等,两个条件同时满足时,表达式为True. switch中break的作用: 如果一个case后面的语句,没有写break,那么程序会向下执行,而不会退出: 例如:当满足条件的case 2下面没有break时,case 3也会执行 1 var num = 2; 2 switch(num){ 3 case 1: 4 alert('case 1'); 5 break; 6 c

老男孩教育每日一题-2017年5月11-基础知识点: linux系统中监听端口概念是什么?

1.题目 老男孩教育每日一题-2017年5月11-基础知识点:linux系统中监听端口概念是什么? 2.参考答案 监听端口的概念涉及到网络概念与TCP状态集转化概念,可能比较复杂不便理解,可以按照下图简单进行理解? 将整个服务器操作系统比喻作为一个别墅 服务器上的每一个网卡比作是别墅中每间房间 服务器网卡上配置的IP地址比喻作为房间中每个人 而房间里面人的耳朵就好比是监听的端口 当默认采用监听0.0.0.0地址时,表示房间中的每个人都竖起耳朵等待别墅外面的人呼唤当别墅外面的用户向房间1的人呼喊时

JavaScript一些重要知识点结合题目的表现!

function Foo() { //① 声明一个Foo的函数 getName = function () { alert (1); }; return this; } Foo.getName = function () { alert (2);}; ② 为Foo创建一个叫getName的静态属性存储一个匿名函数 Foo.prototype.getName = function () { alert (3);}; ③为Foo的原型对象创建一个叫getName的匿名函数 var getName =

学完了js的知识,一起分享总结知识点

又一个知识点学完了,到了总结学习效果和知识总结的时间了.js这个编程语言相对于html和css的逻辑性要强一些,也比较不容易上手.概念性的知识点不难理解,就是实际的操作并不容易,需要通过学习和借鉴案列来理解和帮助并提高实践操作的能力,把理论知识更好的结合到实践当中去,这样才能更有利于去理解和提高自己,做到知识的真正转化,缺乏理论概念性的支撑,有时真的很难上手,尤其是对于刚学习新手而言.所以需要总结知识点,大家可以互相分享一下学习的方式方法,知识总结,通过这种方式方法,相信可以有效的帮助解决一些学

C#高级知识点概要(1) - 委托和事件

作者:linybo 要成为大牛,必然要有扎实的基本功,不然时间再长项目再多也很难有大的提升.本系列讲的C# 高级知识点,是非常值得去撑握的,不仅可以让你写代码时游刃有余,而且去研究和学习一些开源项目时,也不会显得那么吃力了. 希望大家记住,这里讲的所有的知识点,不仅仅是了解了就可以了,还要会灵活用,一定要多思考,撑握其中的编程思想. 本文讲的是委托和事件,这两个词可能你早就耳熟能详,但你是否真正撑握了呢? 本系列讲的C#高级知识点都是要求开发时能达到可以徒手写出来的水平(不依赖搜索引擎.找笔记等

关于Less,你必知的知识点

这是一篇关于Less学习教程 http://www.maiziedu.com/course/497/,讲解了Less的语法,Less的混合等知识点. 1. 关于 less sass 的预编译处理器 LESS 将 CSS 赋予了动态语言的特性,如 变量, 继承, 运算, 函数. LESS 既可以在 客户端 上运行 (支持IE 6+, Webkit, Firefox),也可以借助Node.js或者Rhino在服务端运行. less 编译使用前期使用koala 编译 2. less的 语法 A): 注

JavaScript 总结几个提高性能知识点

前段时间花时间看了大半的<High Performance JavaScript>这本书啊,然后就开始忙项目了,庆幸最忙的一周已经熬过去了.由于空不出时间,这个月写的学习笔记也不多,忙完最苦X的一周,这两天晚上也算是挑灯夜读了...终于是在残血之际将这本书shut down了... 既然读完了,总归是要学到些什么的.说说对这本书的看法先吧,整体的来说,内容还是不错的,就是感觉有点老了(作为前端小白,也可能是自身水平有限,未能体会到其中真意).看这本书的过程中也是写了挺多代码用以测试的,并且对本

Oracle 相关知识点结构图

最近在学Oracle数据库,制作了些结构图方便记忆!主要涉及到Oracle数据类型,Oracle的表操作以及Oracle的游标,还有的之后再分享...... Oracle 数据类型 因为图片上只能看到结构,一些知识点看不了,建议大家点击这个链接,去看源文件:http://naotu.baidu.com/viewshare.html?shareId=atvuh8jmlb4g Oracle表操作 链接:http://naotu.baidu.com/viewshare.html?shareId=atv