C++高级编程 第八章: 掌握类和对象
2017-10-30 18:53
225 查看
本章主要介绍类的基本构造和基本使用.包括 构造函数, 拷贝构造函数, 赋值构造函数
怎么访问成员就不细说了, 还有怎么编写该类的方法, 也不细说了,网上很多教程.
每个常规的方法调用都会传递调用该方法的对象的一个指针, 将其作为隐藏的第一个参数.在函数体里面, 可以直接调用this来获取调用该方法的对象.
首先理解程序的内存分配:
一个由C/C++编译的程序占用的内存分为以下几个部分
栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收.注意它与数据结构中的堆是两回事,分配方式倒是类似于链表.
全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域.程序结束后由系统释放
A对象是栈上的对象, 我们创建一个对象像声明简单变量一样, 就像 int a = 1 ; 所以采用”.”操作符
B对象是堆上的对象, 我们创建一个对象使用到new方法. 用New动态分配内存给B. 所以采用”->”操作符, “->”包含了两个作用 一个是”*” 解引用, 一个是”.” 完成成员或者方法访问.
具体有两种构造形式, 成员初始化列表和构造函数体. 下面就来说说两者的区别.
两者的差别:
函数体构造: 是先定义变量, 再给变量赋值.
初始化列表: 在定义的时候直接给变量赋值, 两者没有先后的顺序
因为先后顺序的问题,所以在以下情况必须使用初始化列表:
初始化一个引用的成员变量
初始化一个const变量
当我们在初始化一个子类对象的时候,而这个子类对象的父类有一个显示的带有参数的构造函数
当调用一个类类型成员的构造函数,而它拥有一组参数的时候
初始化一个引用的成员变量: 当一个引用的成员变量产生时, 需要立刻对变量进行赋值, 就是定义和赋值 要同时进行, 不能有先后顺序.
初始化一个Const变量: Const变量也是 要定义的时候就要赋值, 不可以有先后顺序.
因为子类对象包括父类对象,父类对象既然明确指定了带参的构造函数,那么就必须在构造子类对象的父类部分时显示调用这个构造函数,是不能依赖于合成的默认构造函数的,而这样的话,就必须在成员初始化列表中调用
类型的成员所在类如果有”显示定义的构造函数”那么也是需要在定义这个成员的同时需要显示调用的。
性能的差别
函数体构造流程:
1.执行string的构造函数
2. 定义一个temp变量接收”0”
3. 调用赋值操作符函数去改变con
4. 调用string析构函数
5. 将num设置为0
初始化列表流程
1.直接get到con变量并且set为0
2.直接将num变量set为0
所以综合来看: 初始化成员列表更加方便构造函数的实现 和 更加效率. 所以建议使用初始化列表来写构造函数
tips: 数据成员会按其出现再类定义中的顺序去初始化, 而不是按照初始化列表的顺序.
书中定义: “拷贝”只会出现再对象初始化时才会出现, 如果一个对象已经存在或者已经 有值, 那么如果想重写或者覆盖这个对象, 就需要一个赋值函数来指引.
其实可以理解 A=B; A就是一个已经存在的对象, 而B 就是我们想get到的对象, 所以我们要定义这个”=”, 这个”=” 里面 就是怎样将懒对象赋值 给 另一个对象. 调用这个”=”就是lhs, 而源对象就是右边, 称为”rhs”.
然后我们看函数结构 : 它返回的是一个对象的引用, 这里有两个问题, 第一个就是为什么返回对象的引用, 第二个就是 既然是引用,那么修改A的话B也会修改吗?
第一个问题: 如果不把引用作为返回值, 而是直接返回对象的话, 在函数结束时, 会生成一个temp的对象, 用来存储返回的对象, 然后再把这个temp对象赋值给A,所以中间存在两次赋值, 而采取引用的方式可以 减少这种降低效率的行为.
第二个问题: 测试完再补充.
就是这么理解:
A a;
A b(a);//b 是不存在的!!它只是一个新的内存对象区 , 调用拷贝构造函数
A c=a;//c 是不存在的!!它只是一个新的内存对象区 , 调用拷贝构造函数
A d;
d=a; //d 是存在的, 调用赋值函数
**
当一个对象不存在时, 用别的对象来进行初始化, 就调用拷贝构造函数.
例如:
当对象作为函数参数进行值传递的时候. 就相当于 A c=a;
当对象作为函数返回值进行值的返回的时候. 就相当于 A c=a;
当一个对象需要另外一个对象进行初始化的时候, 相当于 A c=a; A b(a);
当一个对象已经存在, 用别的对象进行初始化的时候, 就用赋值函数.
1. 类的基本构造
类基本包括构造函数和一个析构函数, 这个是最简单的类
class Number{ public: Number(); ~Number(); protect: int num; };
怎么访问成员就不细说了, 还有怎么编写该类的方法, 也不细说了,网上很多教程.
2.this指针
int Number::Count(int a){ this.num+=a; }
每个常规的方法调用都会传递调用该方法的对象的一个指针, 将其作为隐藏的第一个参数.在函数体里面, 可以直接调用this来获取调用该方法的对象.
3.了解堆和栈
我们平时在创建对象或者创建内置类型时, 可以有两种方式创建. 一种时堆上创建, 另外一种时栈上创建.首先理解程序的内存分配:
一个由C/C++编译的程序占用的内存分为以下几个部分
栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收.注意它与数据结构中的堆是两回事,分配方式倒是类似于链表.
全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域.程序结束后由系统释放
Number A A.Count(1); Number B = new Number; B->Count(1);
A对象是栈上的对象, 我们创建一个对象像声明简单变量一样, 就像 int a = 1 ; 所以采用”.”操作符
B对象是堆上的对象, 我们创建一个对象使用到new方法. 用New动态分配内存给B. 所以采用”->”操作符, “->”包含了两个作用 一个是”*” 解引用, 一个是”.” 完成成员或者方法访问.
4. 构造函数
定义: 构造函数我的理解就是: 一个类 它的对象生成时, 就需要构造函数帮它实现这个生成的过程具体有两种构造形式, 成员初始化列表和构造函数体. 下面就来说说两者的区别.
class Number{ public: Number(int a):num(a){} ~Number(); private: int num; }//这个是成员初始化列表
class Number{ public: Number(int a){num = a;}; ~Number(); private: int num; }//这个是函数体构造
两者的差别:
函数体构造: 是先定义变量, 再给变量赋值.
初始化列表: 在定义的时候直接给变量赋值, 两者没有先后的顺序
因为先后顺序的问题,所以在以下情况必须使用初始化列表:
初始化一个引用的成员变量
初始化一个const变量
当我们在初始化一个子类对象的时候,而这个子类对象的父类有一个显示的带有参数的构造函数
当调用一个类类型成员的构造函数,而它拥有一组参数的时候
初始化一个引用的成员变量: 当一个引用的成员变量产生时, 需要立刻对变量进行赋值, 就是定义和赋值 要同时进行, 不能有先后顺序.
初始化一个Const变量: Const变量也是 要定义的时候就要赋值, 不可以有先后顺序.
因为子类对象包括父类对象,父类对象既然明确指定了带参的构造函数,那么就必须在构造子类对象的父类部分时显示调用这个构造函数,是不能依赖于合成的默认构造函数的,而这样的话,就必须在成员初始化列表中调用
类型的成员所在类如果有”显示定义的构造函数”那么也是需要在定义这个成员的同时需要显示调用的。
性能的差别
class Number{ public: Number(){con=0,num=0}; ~Number(); private: string con; int num; }
函数体构造流程:
1.执行string的构造函数
2. 定义一个temp变量接收”0”
3. 调用赋值操作符函数去改变con
4. 调用string析构函数
5. 将num设置为0
初始化列表流程
1.直接get到con变量并且set为0
2.直接将num变量set为0
所以综合来看: 初始化成员列表更加方便构造函数的实现 和 更加效率. 所以建议使用初始化列表来写构造函数
tips: 数据成员会按其出现再类定义中的顺序去初始化, 而不是按照初始化列表的顺序.
5. 拷贝构造函数
class Number{ public: Number(); Number(const Number& temp) :num(temp.num){}//拷贝构造函数 ~Number(); private: int num;b }
拷贝构造函数为什么要const 和 取引用: 因为const能够避免再拷贝的过程中对temp对象修改, 而采取引用不采取传值: 因为传值要对 值进行复制 , 而引用不需要, 只需要取其地址, 能够提高效率. 书中定义: 拷贝构造函数是C++独有的,它是一种特殊的构造函数,用基于同一类的一个对象构造和初始化另一个对象。允许创建一个对象作为另一个对象的完全副本. 对于拷贝构造函数里面的数据成员 会采取 他们对应的拷贝构造函数.
6.赋值函数
class Number{ public: Number(); Number(const Number& temp) :num(temp.num){}//拷贝构造函数 Number& operator=(const Number& rhs);//赋值函数 ~Number(); private: int numb }
书中定义: “拷贝”只会出现再对象初始化时才会出现, 如果一个对象已经存在或者已经 有值, 那么如果想重写或者覆盖这个对象, 就需要一个赋值函数来指引.
其实可以理解 A=B; A就是一个已经存在的对象, 而B 就是我们想get到的对象, 所以我们要定义这个”=”, 这个”=” 里面 就是怎样将懒对象赋值 给 另一个对象. 调用这个”=”就是lhs, 而源对象就是右边, 称为”rhs”.
然后我们看函数结构 : 它返回的是一个对象的引用, 这里有两个问题, 第一个就是为什么返回对象的引用, 第二个就是 既然是引用,那么修改A的话B也会修改吗?
第一个问题: 如果不把引用作为返回值, 而是直接返回对象的话, 在函数结束时, 会生成一个temp的对象, 用来存储返回的对象, 然后再把这个temp对象赋值给A,所以中间存在两次赋值, 而采取引用的方式可以 减少这种降低效率的行为.
第二个问题: 测试完再补充.
7.区分何时用拷贝构造函数和赋值函数
拷贝构造函数是一个对象初始化一块内存区域,这块内存就是新对象的内存区,而赋值函数是对于一个已经被初始化的对象来进行赋值操作。就是这么理解:
A a;
A b(a);//b 是不存在的!!它只是一个新的内存对象区 , 调用拷贝构造函数
A c=a;//c 是不存在的!!它只是一个新的内存对象区 , 调用拷贝构造函数
A d;
d=a; //d 是存在的, 调用赋值函数
**
总结情况: 看别人的博客总结的!!!
**当一个对象不存在时, 用别的对象来进行初始化, 就调用拷贝构造函数.
例如:
当对象作为函数参数进行值传递的时候. 就相当于 A c=a;
当对象作为函数返回值进行值的返回的时候. 就相当于 A c=a;
当一个对象需要另外一个对象进行初始化的时候, 相当于 A c=a; A b(a);
当一个对象已经存在, 用别的对象进行初始化的时候, 就用赋值函数.
相关文章推荐
- C++进阶之二:《C++高级编程》掌握类和对象 阅读笔记
- 第八章,缓冲区对象:存储尽在掌握中
- 《Entity Framework 6 Recipes》中文翻译系列 (44) ------ 第八章 POCO之POCO中使用值对象和对象变更通知
- 《Entity Framework 6 Recipes》中文翻译系列 (46) ------ 第八章 POCO之领域对象测试和仓储测试
- 第八章 浏览器对象document
- Android Application对象必须掌握的七点
- Java第三课 Java中包的概念,类的说明符、方法的说明符、对象的销毁(JVM垃圾会受器的演示),Java中接口理解与掌握。
- Android Application对象必须掌握的七点
- Java中10个流对象重点掌握
- C++沉思录第八章算数表达式树的面向对象问题的分析
- 第八章:在Spark集群上掌握比较重要的图操作之Property Operators(2)
- 流程的Python 第八章:对象引用、可变性和垃圾回收
- Android Application对象必须掌握的七点
- 第八章(对象工厂)
- 掌握对象的程序结构2
- Android Application对象必须掌握的七点
- Java学习日记之类和对象还要掌握:抽象类和接口
- Android Application对象必须掌握的七点
- 掌握VB中的ADO数据对象编程
- 掌握对象的程序结构2