候捷-C++面向对象高级开发
笔记参考
一些候捷C++视频比较完善的学习笔记,可以参考学习一下:
学习目标
- 培养正规的、大气的编程习惯
- 以良好的方式编写C++ class (没有指针成员——complex.h示例、有指针成员——string.h示例)——基于对象
- 学习Class之间的关系继承、复合、委托(opp-demo.h示例)——面向对象
complex类
构造函数
使用构造函数的初值列初始化类的成员变量,参考C++构造函数初始化列表与赋值。
适当的做法:
//构造函数
complex(double r = 0, double i = 0)
:re(r),im(i)//初值列,初始列
{ }
不适当的做法:
//构造函数
complex(double r = 0, double i = 0)
{re=r;im=i;}//赋值
注:不带指针的类大部分不需要析构函数。
常量成员函数
在类的成员函数说明后面可以加const关键字,则该成员函数为常量成员函数。常量对象,以及常量对象的引用或指针都只能调用常量成员函数,参考 C++之常量成员函数。
成员函数在不修改变量的情况下尽量加上const:
double real() const {return re;}
double imag() const {return im;}
参数传递
成员函数中参数传递尽量使用引用,参数传递方式主要有以下几种:
- 值传递:不推荐,只有参数内存大小在4个字节以内才会使用,参数传递语法如 (Complex add)
- 引用传递:推荐,相对于传指针但比指针更优雅,参数传递语法如 (Complex& add)
- 常量引用传递:根据需求使用,加了const后引用参数的值不能被函数修改否则编译器会报错,参数传递语法如 (const Complex& add)
函数返回值
成员函数的返回值也尽量使用引用(建立在非局部变量的情况下),函数内申明的局部变量不能使用引用返回。
inline Complex& _doapl(Complex* ths, const Complex& add)
{
ths->re += add.re;
ths->im += add.im;
return *ths;
}
注:使用引用返回值时,传递者无需知道接受者是以引用还是值的方式接受。
临时对象
参考 二十一、C++中的临时对象:
- 直接调用构造函数将产生一个临时对象
- 临时对象的生命周期只有一条语句的时间(过了这条 C++ 语句,临时对象将被析构而不复存在)
- 临时对象的作用域只在一条语句中
//typename()创建一个临时对象
inline Complex operator + (const Complex& x, const Complex& y)
{
return Complex(x.real() + y.real(), x.imag() + y.imag());
}
友元
在类中指定的友元就可以访问该类中受保护的内容,成员函数、类、全局函数都可以做为友元,参考 C++ 友元。
需要注意,相同class的各个objects互为friends(友元)。
string类
三大函数
拷贝构造函数(copy ctor):若自定义的类中有指针则要自己定义拷贝构造函,系统默认的构造函数只会浅拷贝(将指针拷贝)。
//拷贝构造函数
inline
String::String(const String& str)
{
m_data=new char[ strlen(str.m_data) + 1 ];
strcpy(m_data, str.m_data);
}
String s1("hello");
String s2(s1); //直接取另一个object的private data(兄弟之间互为friend)
拷贝赋值函数(copy assignment operator):将原来的空间清空,分配一个和拷贝的对象的一样大的空间,将内容拷贝过去。
//拷贝赋值函数
inline
string& string::operator = (const string& str)
{
//检查是不是在自我赋值,不仅仅为了提高效率,而且是为了正确性,假设发生自我赋值,会发生错误,指向一个已删除的对象
if (this == &str) { return *this; }
//把指针指的老值杀了
delete[] m_data;
//建立新地盘
m_data = new char[strlen(str.m_data) + 1];
//给新地盘放上复制的新值
strcpy(m_data, str.m_data);
return *this;
}
String s2 = s1;
析构函数:析构函数会在此类对象被释放时自动执行,如离开作用域时。
inline
String::~String() //离开作用域,调用析构函数
{
delete[] m_data; //释放掉因创建对象动态分配的内存
}
带指针的类如果使用默认的拷贝构造函数、拷贝赋值函数会产生内存泄漏、别名的严重错误,如下图所示:
堆、栈与内存管理
栈(Stack):存在与某作用域(scope)的一块内存空间(memory space),如当调用函数时,函数本身即会形成一个stack用来放置它所接收的参数,以及返回地址。
在函数本体(function body)内声明的任何变量,其所使用的内存块都取自上述stack,离开作用域的时候会自动释放。
堆(Heap):由操作系统提供的一块 global 内存空间,程序可动态分配(dynamic allocated)从其中获得若干区块(blocks)。
当离开作用域{}的时候,动态分配的内存不会消失,即从堆中动态取得的内存不会自动消失,需要手动释放(delete 掉)。
静态对象:一个对象前面加上 static 修饰符后,既变成所谓的静态对象(static object),其生命在作用域(scope)结束之后仍然存在,直到整个程序的结束。
全局对象:定义在任何作用域或者说大括号之外的对象,其生命在整个程序结束之后才结束,也可以把它视为一种static object,其作用域是整个程序。
new这个动作被编译器分解为分配内存、转换类型、调用构造函数三个动作:
Complex *pc;
void* mem = operator new( sizeof(Complex) ); //分配内存
pc = static_cast<Complex*>(mem); //强制类型转换
pc->Complex::Complex(1,2); //构造函数
与new对应的是delete,编译器解释为调用析构函数、释放内存两个动作:
Complex::~Complex(pc); //析构函数
operator delete(pc); //释放内存
如果对动态分配所得的内存块(in VC)感兴趣,可以参考堆、栈与内存管理。
扩展补充:类模板、函数模板及其他
关于类方数据成员、静态成员、函数、静态函数:
- 类的数据成员定义了类的对象的具体内容,每个对象有自己的一份数据成员拷贝。修改一个对象的数据成员,不会影响其他本类的对象。
- 类的静态数据只有一份,静态数据在类内部声明后需要在类外部定义。
- 成员函数只有一个,但它要处理很多个对象,this会传递进成员函数,this(本质为指针)告诉成员函数在什么时候该处理哪一个对象。
- 静态函数与非静态函数的差别在于静态函数没有this,只能处理静态数据。
如果函数中存在静态变量,只有函数被调用时函数中的静态变量才会开辟地址,如懒汉式单例模式如下:
class A {
public:
static A& getInstance();//getInstance()是对外的唯一窗口
setup(){ ... }
private:
A();
A(const A& ths);
... //相比singleton,这里的static A a;放到了下面的getInstance()中
};
A& A::getInstance()
{ //如果没有人使用getInstance()那a就不存在
static A a;//只有调用getInstance()对象a才会被创建
return a;//离开这个函数对象a还存在并未死亡
}
一个关于类模板(class template)的示例:
template<typename T>//目前T还没有绑定具体数据类型,T只是个符号
class complex
{
public:
complex (T r = 0, T i = 0) : re(r),im(i)
{ ... }
complex& operator += (const complex&);
T real () cosnt { return re; }
T imag () cosnt { return im; }
private:
T re, im;
friend complex& _doapl(complex*, const complex&);
};//谨记勿忘分号
{
complex<double> c1(2.5, 1.5);//用double替换上述全部T
complex<int> c2(2,6);//用int替换上述全部T
...
}
一个关于函数模板(function template)的示例:
stone r1(2,3), r2(3,3), r3;
r3 = min(r1, r2);//函数模板不需要像类模板中明确指出绑定类型,如complex<int> c2(2,6);绑定类型为int.函数模板会进行实参推导
template <class T>
inline
const T& min(const T& a, const T& b)
{
return b < a ? b : a;//编译器不知道如何比较stone b和stone a(要进行操作符重载),设计这个比大小的人,责任不在他身上,而在设计stone的人.
//在C++中的算法全部都是function template
}
继承、复合、委托
Composition(复合):是has-a关系,表示‘我’里面有另外的东西,在UML图中用组合来描述这种关系。
注:UML用实心的菱形+实线箭头来表示组合(Composition),组合表示部分和整体的关系且生命周期是相同的,如:人与手 。
Delegation(委托):也叫Composition by reference(两个类通过引用(指针)复合),是一种有点虚的has-a关系,指针指向的对象可能随时变换,在UML图中用聚合来描述这种关系。
注:UML用空心的菱形+实线箭头来表示 聚合(Aggregation),聚合表示一种弱的“拥有”关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分,如:公司和员工。
Inheritance(继承):是is-a关系,继承方式public、private、protected有3种,最重要的情况就是public,数据是可以完整的继承下来的。
以上三种情况下构造函数、析构函数都遵循构造由内而外、析构由外而内的顺序执行。
虚函数与多态
虚函数、纯虚函数、非虚函数的区别如下:
- virtual(虚)函数:希望devired class(子类)最好去重新定义(overide),且对它(指父类的虚函数)已有默认定义。
- pure virtual(纯虚)函数:希望devired class(子类)一定重新定义(overide),对它(指父类的虚函数)完全没有默认的定义(其实可以由定义,但你不去定义它)。
- non-virtual函数(不是虚函数):希望drived class(子类)不要重新定义(overide,覆盖)。
虚函数、纯虚函数示例如下:
一个非常经典的设计(通过子类对象调用父类函数):
注:myDoc调用父类的OnFileOpen(),myDoc(谁调用我的那个谁)就会成为隐藏的this pointer。从编译器的角度考虑,它会这样写CDocument::OnFileOpen(&myDoc);&myDoc,即myDoc的地址是隐藏的参数,它将被传到父类中的OnFileOpen()函数的参数中。this->Seirialize();通过this 调用Seirialize(),tthis就是&myDoc。
候捷-C++面向对象高级开发的更多相关文章
- C++面向对象高级开发课程(第三周)
一,类与类之间的关系:继承(Inheritance).复合(Composition).委托(Delegation). 二,复合:表示 is-a ,该设计思想可以参照C语言的 struct . 1. 例 ...
- C++面向对象高级开发课程(第二周)
1. 类中含有指针—— class with pointer member(s) ——的情况经常发生,典型的有:string 类. 2. STL中的 string 类太复杂,copy on write ...
- C++面向对象高级开发课程(第一周)
0. 内存分区 计算机中的内存在用于编程时,被人为的进行了分区(Segment),分为: -“栈区”(Stack) -“堆区”(Heap) -全局区(静态 区,Static) -文字常量区和程序代码区 ...
- C++面向对象高级编程(九)Reference与重载operator new和operator delete
摘要: 技术在于交流.沟通,转载请注明出处并保持作品的完整性. 一 Reference 引用:之前提及过,他的主要作用就是取别名,与指针很相似,实现也是基于指针. 1.引用必须有初值,且不能引用nul ...
- C++面向对象高级编程(八)模板
技术在于交流.沟通,转载请注明出处并保持作品的完整性. 这节课主要讲模板的使用,之前我们谈到过函数模板与类模板 (C++面向对象高级编程(四)基础篇)这里不再说明 1.成员模板 成员模板:参数为tem ...
- C++面向对象高级编程(七)point-like classes和function-like classes
技术在于交流.沟通,转载请注明出处并保持作品的完整性. 1.pointer-like class 类设计成指针那样,可以当做指针来用,指针有两个常用操作符(*和->),所以我们必须重载这两个操作 ...
- C++面向对象高级编程(六)转换函数与non-explicit one argument ctor
技术在于交流.沟通,转载请注明出处并保持作品的完整性. 1.conversion function 转换函数 //1.转换函数 //conversion function //只要你认为合理 你可以任 ...
- C++面向对象高级编程(五)类与类之间的关系
技术在于交流.沟通,转载请注明出处并保持作品的完整性. 本节主要介绍一下类与类之间的关系,也就是面向对象编程先介绍两个术语 Object Oriented Programming OOP面向对象编 ...
- C++面向对象高级编程(四)基础篇
技术在于交流.沟通,转载请注明出处并保持作品的完整性. 一.Static 二.模板类和模板函数 三.namespace 一.Static 静态成员是“类级别”的,也就是它和类的地位等同,而普通成员是“ ...
- C++面向对象高级编程(三)基础篇
技术在于交流.沟通,转载请注明出处并保持作品的完整性. 概要 一.拷贝构造 二.拷贝赋值 三.重写操作符 四.生命周期 本节主要介绍 Big Three 即析构函数,拷贝构造函数,赋值拷贝函数,前面主 ...
随机推荐
- js转化文章发布于几天几小时几分钟前
alert(dateFormat('2020-07-08 11:32:44')); function dateFormat(d1) { var dateEnd = new Date();//获取当前时 ...
- KB0003.申请和加载DoraCloud的软件许可
KB0003.申请和加载DoraCloud的软件许可 DoraCloud安装后,默认处于30天试用状态.如果您购买了软件授权,可以申请许可证. 在[系统][License管理][获取License文件 ...
- frmClientDm.ItemInLogShowAdq.Delete 报【BOF 或 EOF 中有一个是“真”,或者当前的记录已被删除,所需的操作要求一个当前的记录。】
当Adoquery 中只有一条数据库的时候,这个时候删除 就会报 :BOF 或 EOF 中有一个是"真",或者当前的记录已被删除,所需的操作要求一个当前的记录.这个错误 导致这个错 ...
- Go 之烧脑的接口
基本定义 Go 官方对于接口的定义是一句话:An interface type is defined as a set of method signatures. 翻译过来就是,一个接口定义了一组方法 ...
- 【简写MyBatis】01-简单映射器
前言 新开一个坑,为了学习一下MyBatis的源码,写代码是次要的,主要为了吸收一下其中的思想和手法. 目的 关联对象接口和映射类的问题,把 DAO 接口使用代理类,包装映射操作. 知识点 动态代理 ...
- UVA12024 Hats 题解
题目传送门 前置知识 错位排列 题意 有 \(t\) 组询问,每次询问给定一个 \(n\),表示有 \(n\) 个人,每人各有一个属于自己的帽子,求所有人都带错帽子的概率(不要求约分至最简形式). 解 ...
- NC15447 wyh的问题
题目链接 题目 题目描述 我国现在能源消耗非常严重,现在政府有这样一个工作,每天早上都需要把一些路灯关掉,但是他们想让在关闭的过程中所消耗的能源是最少的,负责路灯关闭的工作人员以1m/s的速度进行行走 ...
- Transform LiveData
查询资料的其中一个场景: 创建一个回调函数,当查询后台的时候,后台有结果了,回调对应的回调函数,并将结果保存到LiveData中. public class DataModel { ... ...
- 【Unity3D】灯光组件Light
1 灯光简介 在 Hierarchy 窗口右键,选择 Light,再选择具体的灯光类型,在 Inspector 窗口查看灯光组件如下: Type:灯光类型,主要有:Directional(平行光) ...
- dllimport 和 dllexport
Dll 在需要暴露接口的头文件里添加 dllexport 声明,比如, #define DllExport __declspec( dllexport ) class DllExport C { in ...