首页 文章 精选 留言 我的

精选列表

搜索[开源周],共10000篇文章
优秀的个人博客,低调大师

C++面向对象高级编程(下) 第一周

Conversion Function 转换函数 你现在设计一个对象 class A,它可不可以被转为另外一种类型,这就是一种转换。或者是,另外一种类型可不可以转为A。一个是转出去,一个是转进来,这两个方向,我们都要谈。 现在首先谈的是转出去: 1、转化函数不可以有参数。转换类型而已,那有什么参数可言。 2、转化函数没有返回类型,返回类型就是operator后面的double。他也没有参数。 3、这种转化函数通常会加上const,因为并没有改变值,如上图黄色的那块函数。不写也不会报错,但是以后的情况有可能会出错。 4、转换函数可以写任何一个type都可以,不一定是基本类型,只要编译器认得就可以。 5、对于上面的调用(),编译器会先去找一个有没有“+”重载,重载函数的两个参数里面一个是int型,一个是fraction类型。编译器没找到,所以就看看有没有转换函数,把f转成double型,于是就找到了上图中黄色的函数。 任何一个class,只要你认为合理,你都可以写很多个转换函数。 未完待续 2018.11.24

优秀的个人博客,低调大师

C++面向对象高级编程(上) 第二周 侯捷

三大函数——拷贝构造、拷贝赋值、析构函数 拷贝构造——接受的是自己这种东西 ctor和dtor构造函数和析构函数 字符串有两种: 一种是前面有一个常数,用于记录字符串的长度,此字符串的末尾没有结束符号。 另一种是字符串的末尾有结束符号,字符串的开头没有用于记录字符串长度的常数。 new就是分配内存,分配了一个字符的内存。 分配了一个字符的内存,然后把结束符传进来,这样就形成了一个空字符串 strlen是一个函数,获取字符串的长度(strlen是计算机C语言函数,计算字符串s的(unsigned int型)长度,不包括'\0'在内)你的class里面有指针,你多半是要做动态分配。所以你要在他生命结束前,调用析构函数,把内存释放掉) 拷贝赋值函数 如图中的红框①②③,是要把右手里面的东西拷贝赋值给左边的步骤: a)清空左边的东西 b)申请和右边一样大的内存空间 c)拷贝 如果没有上面那句检测自我赋值( ),会出现如下情况: 检测是否为自我赋值,不仅仅是为了效率,还是为了安全性。 output 函数 为了打印类中的东西,我们要重载操作符 "<<",由于成员函数有默认this指针,如果将重载"<<"设置成成员函数,那么变量的位置要发生改变,这不符合人们的使用规范,因此,重载"<<"要设置成全局函数。 ostream& operator<<(ostream& os, const String& str) { os << str.get_c_str(); return os; } 任何东西,只要你能直接丢给cout,你就直接丢给他输出好了。先看一下整体代码: #ifndef __MYSTRING__ #define __MYSTRING__ class String { public: String(const char* cstr=0); String(const String& str); String& operator=(const String& str); ~String(); char* get_c_str() const { return m_data; } private: char* m_data; }; #include <cstring> inline String::String(const char* cstr) { if (cstr) { m_data = new char[strlen(cstr)+1]; strcpy(m_data, cstr); } else { m_data = new char[1]; *m_data = '\0'; } } inline String::~String() { delete[] m_data; } 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; } inline String::String(const String& str) { m_data = new char[ strlen(str.m_data) + 1 ]; strcpy(m_data, str.m_data); } #include <iostream> using namespace std; ostream& operator<<(ostream& os, const String& str) { os << str.get_c_str(); return os; } #endif 注意到 get_c_str函数的返回值是char *,刚刚好可以直接给cout进行输出,因此我们写了get_c_str函数来进行输出。 堆栈与内存管理 stack object 的生命周期 static local object global object 的生命周期 heap object 的生命期 new——先分配内存,后调用构造函数 new的动作分解: a)调用 operator new 函数来分配内存(operator new 底层调用的是malloc)。对应的,上图分配出 b)第二个动作把我们创建的变量做一个类型转换 c)通过指针调用构造函数Complex(注意:构造函数在类里面,所以是成员函数,会有this指针。谁调用成员函数,this指针就指向谁。因此,上图中的第三步完整的写法应该是如图所示的形式:。 这里的this指针指向了pc) delete:先调用析构函数,再释放内存 动态分配内存块 in VC 根据上图第一个矩形(debug模式下的情况): 1、new 一个复数会获得的内存是 8 byte(上图中第一个矩形草绿的部分)。 2、在调试模式下,你会得到灰色的部分,上面每一格是4byte(即),一共 4*8=32个字节。 3、还会得到草绿色矩形下面的那一个 4byte(即) 4、上下两个砖红色的矩形区域是cookie 内存一共需要 8+(32+4)+(4*2)=52,而VC给你分配的内存块一定是16的倍数(现在不提为什么),因此填补了三个深绿色的填补物pad。 看起来分配很浪费,但是这是必要的浪费,因为回收的时候,操作系统需要依据这里的信息来进行回收。 根据上图中第二个矩形(release 模式): 分配内存8byte,加上上下cookie刚刚好16byte,因此无需调整,无需添加填补物pad。 上下cookie的作用 记录整块给你的内存大小,便于系统回收,让系统知道回收多大内存。 让我们看一下cookie上记录的数字——000000041,对于第一个矩形,内存一共分配了64byte,64的16进制表示是40,而cookie上的数字是41,为什么呢?40借助最后一个bit,最后一位,标志我这块内存是给出去了还是收回来了。这里是给出去了,对于程序来说是获得了,所以最后一位标志位1,因此是41。 同理,对于第二个矩形,系统给出的内存是16byte,16的16进制是10,这里cookie上写的是11,因为这是程序获得的内存。 为什么可以借最后一个bit来标志这一块内存是给出了还是收回了? 因为分配的内存都是16的倍数,16的倍数最后四个bit都是0,我们就可以借一位来表示内存的状态。 为什么array new 要搭配 array delete 分析上图中第一个矩形(debug模型下的array new分配的内存分析) 1、复数申请的数组长度是3,因此申请了三块内存() 2、在调试模式下要加上那个header,上面是32下面是4(即和) 3、加上向下cookIe() 4、在VC中(别的编译器不明),会用一个4byte的内存记录数组的长度,即图中的 因此内存分配为 8*3+32+4+4*2+4=72 ,凑16的倍数,所以分配内存为80。 array new要搭配array delete 否则会造成内存泄漏。让我们看看是哪一种内存泄漏 内存泄漏的是动态分配的内存。 写不写 [],清空的内存大小是相等的,因为cookie上有记录。但是不写 [] ,编译器不知道要对每一个对象进行分别的析构。析构的时候,先分别析构各个内存对象,然后再析构母体的那个地方()。发生内存泄漏的是这里() 复习string的实现过程 设计一个class,我总是要去思考我需要什么样的数据。由于不知道字符串的长度,所以大部分人设计字符串这种类中的数据都是在里面放一根指针,将来要分配多大的字符串内容,就动态地去分配字符串的大小,用new的方式去动态分配一块内存(在32位的平台里面,一根指针占内存是4字节,所以不管你里面字符有多长,字符串本身就4个字节的内存) Class里面带指针,所以我要关注三个重要的函数: 拷贝构造:他是一个构造函数,所以没有返回值。他要有一个拷贝蓝本,蓝本就是他自己(传入reference是可以的,又因为我们不会改变蓝本,所以前面可以加一个const) 拷贝赋值:赋值是要把来源段的拷贝到目的端,所以涞源段的内容和拷贝构造是相同的(所以他传入的参数和拷贝构造的参数是相同的) 因为传入的值我们不打算去改变他,所以前面加一个const。 拷贝赋值的返回值(要不要return by reference,要看函数执行所返回的结果是不是放在里local object中,只要不是local object,就可以传reference) 析构函数: 辅助函数:我们希望把最后的结果丢给cout来输出到屏幕上(加了const是因为不会改变数据) 拷贝赋值函数: 涞源段拷贝到目的端,目的端是已经存在的东西,所以 第一个动作应该是把目的端的内存清空 第二个动作是重新分配一块够大的空间: 第三个动作是把来源端拷贝到目的端: 接下来要思考赋值之后的返回值(如果不写返回值的话,连串的赋值行为就会受限) &str得到的是一根指针 String&是引用 扩展补充:类模板、函数模板以及其他 进一步补充:static 谁调用我,谁就是那个this pointer,所以c的地址就是this pointer 成员函数有一个隐藏的参数this pointer,但是我们不能写进去,这个是编译器帮我们写进去 静态数据:加入了static的数据,就跟对象脱离了,他不属于对象,他在内存的某一个区域单独有一份,我们不必知道他在那里,反正后面的代码能够找得到就好了 静态函数:他的身份跟一般的成员函数字内存中是相同的,我们所指的相同指的是他也在内存中只有一份。函数在内存中当然只有一份,不可能因为你创建了好几个对象,就有好几个函数 静态函数跟一般函数的差别就在于,静态函数没有this pointer。因此静态函数如果去处理数据,他只能去处理静态的数据。 例子: 进一步补充:把ctors(构造函数)放在private区 当我们希望写的class只产生一个对象的时候,可以这么用。 把构造函数写在private里面,这样外界就无法再创建对象。 这么写有个缺陷,就是如果外界不需要这个数据,这个数据依然存在,这样会造成内存的浪费。更好的写法如下: 进一步补充:cout 为什么cout可以接受任何类型的数据,因为里面重载了很多 进一步补充:类模板 模板会造成代码的膨胀,但是这并不是缺点,因为你确实是需要这种类型的函数,即使不用模板,你也要写出来 进一步补充:function template,函数模板 类模板在用的时候要明确指出类型( ),函数模板则不需要,因为编译器会做实参的推导(argument deduction) 进一步补充:namespace namespace等同于你把你的东西都封锁在这个命名空间里了,这样就不会打架。 Using directive(使用命令):等同于你把封锁打开,调用的时候就用写全名(e.g std::cin)了,可以直接写cin Using declaration:一行一行的打开,不是全开,因为里面东西可能会很多 或者是都不打开,就每一步都规规矩矩的写全名

优秀的个人博客,低调大师

C++面向对象程序设计课程笔记(第三周)

第一节 this指针 1.C++程序到C程序的翻译 图1 从C++到C C++中的class在C语言中并不存在,因此可以用struct(结构体)来代替,成员变量也可以直接用域来代替,但是类中的成员函数却无法在C语言中直接表示,所以只能把SetPrice设置为全局函数。由于是全局函数,为了能让其使用结构体内的数据,故在SetPrice中增加了一个参数struct CCar *this,这样就可以在函数体中把p的值传递给结构体中的price,实现了跟成员函数一样的功能,同理,在main()中SetPrice函数执行也得把car的地址传递过去。 故,this的作用就是指向成员函数所作用的对象。 2.this指针作用 (1)非静态成员函数中可以直接使用this来代表指向该函数作用的对象的指针。 图2 this指针作用 在main()函数中,首先对对象c1执行了AddOne()函数,然后开始执行AddOne(),对c1中的real加1,然后执行对c1的Print()函数,这之后,返回一个this指针所指向对Complex对象(即c1)给c2。 对this指针再加深一个理解,如下例: class A { int i; public: void Hello() {cout<<“”Hello”<<endl;} }; int main() { A *p=NULL; p->Hello(); } 系统会报错吗?答案是否定的,并且输出Hello。原因在于,虽然我们看着这个*p指向的空,但是实际上,由于C++在转换成C语言的时候,成员函数会被转换成全局函数并增加一个this指针的参数,即变为了 void Hello(A *this){cout <<”Hello”<<endl; 主函数中的p->Hello();也变成了Hello(p); 因此就算p为空指针,在这里传递给全局函数后并没有用到p做任何输出,所以直接输出了Hello。但如果我们在这个函数的输出时用到了成员变量i,那么成员函数在变换到全局函数的时候再来执行就用到了这个this所指向的对象,而此时this指向的p为空,所以报错。 静态成员函数不能使用this指针,因为静态成员函数并不具体作用于某个对象,因此静态成员函数的真是的参数的个数,就是程序中写出的参数个数! 第二节 静态成员变量 1.静态成员基本概念 在定义前面加了static关键字的成员,称为静态成员。静态成员包括静态成员变量和静态成员函数两种。 普通成员变量每个对象有各自的一份,而静态成员变量一共就一份,为所有对象共享。 sizeof运算符在计算类的大小的时候不会计算静态成员变量。 普通成员函数必须具体作用于某个对象,而静态成员函数并不具体作用于某个对象。 因此静态成员不需要通过对象就能访问。 静态成员变量本质上是全局变量!!! 静态成员函数本质上是全局函数!!! 设置静态成员的机制是将和某些类紧密相关的全局变量和函数写到类里面,看上去像一根整体,容易维护和理解。 2.如何访问静态成员 (1)类名::成员名 例如CRectangle::PrintTotal(); (2)对象名.成员名 例如CRectangle r; r.PrintTotal(); (3)指针->成员名 CRectangle *p=&r;p->PrintTotal(); (4)引用.成员名 CRectangle &ref =r; int n = ref.nTotalNumber; 必须在定义类的文件中对静态成员变量进行一次说明或初始化,否则编译能通过,链接不能通过。 在静态成员函数中,不能访问非静态成员变量,也不能调用非静态成员函数。 如下所示的写法,存在缺陷。 CRectangle::CRectangle(int w_,h_) { w = w_; h = h_; nTotalNumber++; nTotalArea += w* h; } CRectangle::~CRectangle(){ nTotalNumber–; nTotalArea -= w*h; } void CRectangle::PrintTotal(){ cout<<nTotalNumber<<“,”<<nTotalArea<<endl; } 在使用CRectangle类时,有时会调用复制构造函数生成临时的隐藏的CRectangle对象,比如:①我们调用了一个以CRectangle类对象作为参数的函数;②调用一个以CRectangle类对象作为返回值的函数。这个时候就如前面介绍复制构造函数时所说,会产生一个隐藏的临时对象(此函数没写复制构造函数,因此就算直接复制值过去,并没有执行Crectangle(int w_,int h_)),而这个临时对象在消亡的时候会调用析构函数,减少了nTotalNumber和nTotalArea的值,而它们这些对象在生成时却没有增加这两个值,由于这两个值是静态成员变量,所以实际上这两个值是减少了。 解决方案很显然就算写一个复制构造函数: CRectangle::CRectangle(CRectangle &r) { w = r.w;h=r.h; nTotalNumber ++; nTotalArea += w*h; } 第三节 成员对象和封闭类 1.基本概念 有成员对象的类交封闭(enclosing)类。 有一个类的成员是其它类的对象,被称为成员对象。 如下例程: class CTyre { private: int radius; int width; public: CTyre(int r int w):radius(r),width(w){}//初始化列表,可以为每一个成员变量提供一个初始值。 }; class CEngine{}; class CCar{//封闭类 private: int price; Ctyre tyre;//成员对象 CEngine engine;//成员对象 public: CCar(int p, int tr,int tw); }; CCar::CCar(int p,int tr,int w):price(p),tyre(tr,w)//engine使用无参构造函数构造 {}; int main() { CCar car(20000,17,225); return 0; } 在这个例子,如果CCar类不定义构造函数,则CCar car;这个语句会报错,因为编译器不明白car.tyre该如何初始化。 任何生成封闭类对象的语句,都要让编译器明白,对象中的成员对象是如何初始化的。 具体做法是:通过封闭类的构造函数的初始化列表进行。 成员对象初始化列表中的参数可以是任意复杂的表达式,可以包括函数、变量,只要表达式中的函数或变量有定义就行。 2.封闭类构造函数和析构函数的执行顺序 (1)封闭类对象生成时,先执行所有对象成员的构造函数,然后才执行封闭类的构造函数; (2)对象成员的构造函数调用次序和对象成员在类中的说明次序一致,与它们在成员初始化列表中出现的次序无关; (3)当封闭类的对象消亡时,先执行封闭类的析构函数,然后再执行成员对象的析构函数。次序和构造函数的调用次序相反。 3.封闭类的复制构造函数 封闭类的对象,如果是用默认复制构造函数初始化的,那么它里面包含的成员对象,也会用复制构造函数初始化。 例如: Class A { public: A(){cout<<“default”<<endl;} A(A&a){cout<<“copy”<<endl;} }; class B{A a;}; int main() { B b1,b2(b1); return 0; } 输出: default copy 说明b2.a是用类A的复制构造函数初始化的,而且调用复制构造函数的实参就是b1.a。 第四节 友元(friends) 友元分为友元函数和友元类两种。 1.友元函数 一个类的友元函数可以访问该类的私有成员(这说明友元函数不是这个类的成员函数) 可以将一个类的成员函数(包括构造、析构函数)说明为另一个类的友元。 Class B{ public: void function(); }; Class A{ friend void B::function(); }; 这就代表function()可以访问A里面的私有成员了。 2.友元类 如果A是B的友元类,那么A的成员函数可以访问B的私有成员。 Class CCar { private: int price; friend class CDriver;//声明CDriver为友元类 }; class CDriver { public: CCar myCar; void ModifyCar(){ myCar.price += 1000; //因为CDriver是CCar的友元类,故可以访问其私有成员 } }; int main(){return 0;} 友元类之间的关系不能传递,不能继承! 第三节 常量对象、常量成员函数和常引用 1.常量成员函数 如果不希望某个对象值被改变,则定义该对象的时候可以在前面加const关键词。例如: class Sample{ private: int value; public: Sample(){} void SetValue(){} }; const Sample Obj;//常量对象 Obj.SetValue();//错误。常量对象只能使用构造函数、析构函数和右const说明的函数(const方法) 在类的成员函数说明后面加const关键字,则该成员函数为常量成员函数。常量成员函数执行期间不应修改其作用的对象。因此,在常量成员函数中不能修改成员变量的值(静态成员变量除外),也不能调用同类的非常量成员函数(静态成员函数除外)。 如下: class Sample{ public: int value; void func(){}; Sample(){} void GetValue() const; }; void Sample::GetValue() const{ value = 0;//wrong func();//wrong } int main(){ const Sample o; o.value = 100;//err常量对象不可被修改 o.func();//常量对象上面不能执行非常量成员函数 o.GetValue();//OK常量对象可以执行常量成员函数 return 0; } 需要注意,在Dev C++中,要为Sameple类编写无参构造函数才可以,在VS中不需要。 2.常量成员函数的重载 两个成员函数,名字和参数表都一样,但是一个是const,一个不是,算重载。 3.常引用 引用前面加const称为常引用。不能通过常引用修改其引用的变量。 可以用对象的引用作为参数,如: class Sample{ }; void PrintfObj(Sample & o){ } 对象引用作为函数的参数有一定风险性,若函数中不小心修改了形参o,则实参也跟着变。为了避免这个问题,我们可以用对象的常引用作为参数,这样就能确保不会出现无意中更改o值的语句了。 class Sample{ }; void PrintfObj(const Sample & o){ }

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册