C++进阶 面向对象基础(三)
类的的定义:
初始化一般建议使用构造函数初始化列表形式:
Person(const string nm, const string addr):name(nm), address(addr){}
this指针:
类中使用this指针,特别是有些情况不能省略,例如在子类的方法中调用父类的某个成员变量,得加上this,不然有些编译器不通过,又例如
class Per{
Per& getName(){
return *this;
}
Per& getAge(){
return *this;
} };
这个调用 Per per; per.getName().getAge();
如果函数也是const, 那么对应的返回也应该是const 并且,可以基于const进行函数重载;
对象的创建,如果是创建一个指针,对必须用new。 构造函数:
引用类型,const类型,没有默认构造函数的类类型的成员,必须再初始化列表中进行初始化。
单参数构造函数需要制定关键字explicit,防止发生隐式转换。
不要在构造函数中,做太多逻辑相关的操作。
为避免使用默认构造函数,可以将其声明了private成员,并且使用宏代替。 友元数,友元类:可以直接调用友元的的私有成员。
友元函数A在类B中声明,A类变为B类的友元类,将A类在B类中声明。
例如:Class A{ friend class B;}; ”表示B是A的友元类,则B可以用A的任意东西“。则可以在B类中可以任意的调用A的私有成员。 static成员:
属于公用的共享的成员,例如一个类,声明了两个对象,这这两个对象的静态成员是公用的、享的。而普通成员则不具有这种特性。。
所以可以充当各个类之间的全局变量,比普通的全部变量好处在于可以避免不同类之间全局变量的干扰,适合某一类数据的全局变量的封装;
静态的变量可以通过类的作用域操作符,直接对静态变量进行初始化,或者使用,Class A{ static float x;} A::x =0.3;
另外静态的成员函数里面不能使用this指针,因为静态的成员函数不属于任何一个对象,因为是公共的;
例如静态的const的整型变量,可以再类中声明的时候直接初始化。其他的都不可以。 复制构造函数,赋值操作符;
以上两个,如果自己没写,C++ 会默认写一个。很多的时候不用自己写,但是当类中有一个成员是指针的时候,一定要自己写一个。
复制构造函数 的参数只有一个,并且一般都是const 类型的当前类 例如 class A{ A{const A &a):x{a.x},y(a.y){}} 复制构造函数将会将当前 对象的成员复制到另外一个对象里。所以可以A a(b);
赋值操作符:当一个类的数据成员有指针,动态分配内存的时候,一般必须写一个赋值构造函数。class A{ A& operation =(const A &b){a.x = b.x; a.y = b.y;}} ; 所以可以直接 a = b;b
复制构造函数:
class A{
public:
A(A& b):p(new std::string (*(b.p))){} //如果掉用的默认的复制构造函数,则这里为A(A& b):p(b.p){} 只是将指针复制给了指针,而我们的目的是将原来的字符串拿到,并用来初始化当前的指针,重新创建一个字符串。
private:
str::string *p;
}
同理赋值操作符为:
A& operation=(const &b){
p = new std::string; //如果用的默认的只是 p = b.p; 并没有用里面的数字进行真正的复制。浅复制
*p = *(b.p);
return *this;
} 析构函数:
如果写了析构函数,就应该写复制构造函数,复制操作符。(三法则) 浅复制与深复制
数据成员是指针,一定要做深复制。 如果类操作了系统的资源,只要动态的分配了资源,复制的时候,也一定要写深复制。 管理指针:(当一个类有指针成员的时候,一定要注意)
常规指针类(浅复制)避免浅复制,一般可用以下方法:智能指针类(计数类)有浅复制的特性,数据共用,但是又不会产生野指针;值型类(深复制)
自己设计智能指针类中的每个成员是私有的,内部有个指针和对应的计数器变量。 重载操作符:
一般不要或者尽量少用操作符重载,如果要使用,可以使用函数去实现。例如==,可以使用Equals,或者CopyFrom去实现。
关键字 operator
操作符重载:
输入输出操作符重载,特别是输入操作符重载要注意检查输入。
算术操作符重载,+一般是非成员函数,+=一般作为成员函数。 操作符重载:(转换操作符)
operator int()const。 必须是成员函数,不能指定返回值,形参表必须是空的,必须显示的的返回一个指定类型的值,通常定义为const类型,防止改变被转化对象。
class A{
public:
A():x(){}
operator int()const{
return x;
}
private:
int x;
}
A a;
int y = a; //这样可以直接将一个类类型自动转换为了一个int类型,然后复制给y了。
打印错误信息:
printf("%s %d %s\n", __FILE__, __LINE__, "operation failed with this");
fflush(stdout);
exit(-1);
操作符重载:(比较操作符)
== != ; >= <=; > <;
一般用这种重载是配套的,定义了一个,也得定义另外一个,并且利用第一个来做第二个,并且第一个一般用友元函数的方式实现,而第二个就可以不用友元方式了。
操作符重载:(赋值操作符)
= += -= *= /= %= 等等
赋值操作符必须返回对*this的引用
操作符重载:(下标操作符)
[] 一般重载的写两个:可变成员函数,常量成员函数,函数里面的东西都是一样,定义const的意义是,如果定义的const,那就要求不能修改。
例如 使用的时候,String s("heelo"); String const s2("dog"); 则这里就是要求s2不能修改,所以类String中还需要重载一个const常量成员函数。
操作符重载:(成员访问操作符)
* -> 主要在定义只能指针的时候,有可能需要重载这两个操作符,普通的类一般是不需要对这两个操作符重载的。
函数对象:
重载函数调用操作符,operator(),函数对象可以在一些算法传递函数对象的地方可以使用,而普通函数则不行。函数对象实际上是一个类,因为类有数据成员,所以可以
保存一定的状态。如果函数对象的返回值是bool,我们叫这个函数对象是谓词。返回一个值,就是一元谓词,二个值,就是二元谓词。
函数对应可以用类做,也可以用结构体做。
template <typename elementType>
struct DisplayElements{
int m_count;
DisplayElements(){
m_count = 0;
}
void operator()( const elementType &a){
m_count++; //这里就比普通的函数多了个计数器,也就是当这个函数对象调用了多少次,可以通过m_count获取。
cout<< a << ' ';
}
};
调用时
DisplayElements mResult;
mResult = for_each(a.begin(), a.end(), mResult<int>); //函数对象确定名称调用的形式
mResult = for_each(a.begin(), a.end(), DisplayElements<int>); //函数对象匿名调用的形式
cout << mResult.m_count << endl; //可以获得比普通函数更多的信息
基类和派生类:
protected和public的成员,在子类中可以直接使用,但是不能在子类中,通过基类去获得对应的protected成员。受保护的不能用基类直接调用,
受保护的成员,专门用于继承使用的,所以受保护的成员,基类是可以直接用的,并且只能在基类定义的内部使用,不能再别的地方直接调用使用。
class base{
public:
int x;
protected:
int y;
private:
int z;
}
class bundle_base :public base{
public:
bundle_base(): base(){}
void display(){
cout << x<< endl;//可以直接调用x
cout << y << endl; //可以直接调用
}
void display(bundle_base &a, base &b){
cout <<a.x<< endl;
cout <<a.y<<endl; //并且这个受保护的成员y,在别的作用域是不能直接调用的,在子类的以为区域,就相当于变成了私有成员了。
?? cout <<b.y <<endl; //这里是不能用的.
}
}
虚函数:可以重写和不重写,纯虚函数:必须重写。
子类重载的虚函数,一般也写上virtual。 动态绑定(多态)
多态性,派生类到基类的转换,引用或指针既可以指向基类对象,也可以指向派生类对象,只有通过引用或指针调用虚函数才会发生动态绑定。 三种继承
一般用public继承,几乎不用protected和private继承,默认是私有继承,java只有public继承。私有继承中,想把基类的public中的类型变回public,
可以使用using,来去除个别成员的私有特性,来修改继承访问。
class A{
public:
int x;
int y;
}
class B : private A{
public:
using A::x; //这里把x变成了公有继承
y; //这里的y,对于B而言,就是private成员了,因为是私有继承。区别上面的(修改继承访问)方式。
} 派生类的构造函数和析构函数
派生继承 类的构造和析构中,构造函数,从先调用基类的构造函数,然互调用成员对象的构造函数,最后调用自己的构造函数。注意:这个过程在构造析构子类的时候,就会发生。
class E : public B, public A, public C{ //构造E的时候,构造函数调用顺序为 B,A,C,D,E的构造函数。析构则相反顺序
private:
D d;
} 转换和继承:
引用转换/指针转换 对象转换 (派生类到基类) 把派生类传给基类,如果是对象传递,则,无法实现多态。
,void (Base a){a.function();} , void(Base &a){a.function();} ,void(Base *a){a->function();};如果将一个子类 传递给基类Base,
则如果是 对象传递1,则使用使用基类的function,无法实现多态的意义,所以一般用2和3,引用和指针传递。
如果是基类转换到子类,一把是禁止的,如果要进行,则需要用强制转换。 友元和继承:
友元可以访问类的private和protected成员。但是友元关系不能继承。 静态成员和继承:
基类中的static成员,在整个继承层次中只有一个实例。
访问方式:基类名::成员名 子类名::成员名 对象.成员名 指针->成员名 成员名(在子类中,只要有访问权限) 纯虚函数和抽象类:
含有纯虚函数的类为抽象类, 纯虚函数是虚函数声明后面加上"=0;",纯虚函数的定义可以写可不写,一般不写,让子类来实现;
抽象类不能创建对象,即不能实例化,只能继承;纯虚函数必须实现;只具有纯虚函数的抽象类成为c++接口。
具有纯虚函数的类的子类,对应的函数也一定是虚函数,所以对应的析构函数也必须是虚函数,但是对应的子类不是抽象类了,既可以实例化其对象。(因为具有虚函数的类的析构函数必须是虚函数) 模板与泛型编程:
类模板和函数模板;模板编程又称泛型编程。 队列:顺序队列
push pop front Rear isEmpty等操作,顺序队列是用数组做的队列,中途new空间。如果大小改变,得重新分配空间。
队列:链式队列
使用链表做的队列,比顺序队列更灵活,设计更加简单。 函数模板:
函数模板->实例化->函数。 使用模板形参 template <typename T> 一般可以实现代码复用。 异常:
try catch throw 异常类型:数字,字符串,类对象。
比如出错了,不用return 返回各种不同数值,然后判断数值的方式, 而是用thow来抛出异常。抛出的异常可以用数字对象和字符串等。
int isXEqual() {if(x==y) thow ;} 或者thow “failed”; 等方式来跑出异常。调用的时候可以用
tyr{ isXEqual();}
catch(int e){ printf(”异常 %d\n“, ); } 当发生异常的时候,会运行catch部分,比这种return来检查出错结果是啥,会好很多。
catch(...){printf("exeption\n");} catch所有异常用‘...’代替。
异常():自己创建异常类
在类中创建异常类,类名一般用xName的形式(x开头的名字),使用的时候,直接throw xName;就可以了。
class array{
private:
size_t itsSize;
public:
array(size_t x):itsSize(x){}
class xBase{
public:
virtual void printError(){printf("the exeption comes\n");}
}
class xName : public xBase{
virtual void printError(){printf("the exeption name comes\n");}
};//如果需要写多个异常类,返回多个不同的异常,可以使用多态方法;继承一个类,然后在基类中使用虚函数的方式,获取基类的异常即可
class xSize : public xBase{
virtual void printError(){printf("the exeption size comes\n");}
}
void getSize(){
if(size>) throw xSize();
if(itsSize <) throw xName();
}
};
调用的时候可以:
try{
array arr();
getSize();
}
catch(array::xBase &exep){ //一定要按引用或者指针传递,多态技术,可以使用一个catch 来捕获所有的异常
exep.printError();
}
catch(array::xName){ //当然也可以单个的使用某个异常,优先使用上面的那个方法
printf("name exeption\n");
}
异常:标准异常
exception runtime_error rang_error overflow_error
underflow_error logic_error domain_error invalid_argument length_error out_of_range bad_alloc(分配空间过多失败的异常)
try{
int x = new int[];
}catch(bad_alloc err){
printf("bad alloc err\n");
} 职能指针:
智能指针是个指针模板类。常常解决:深度复制,写时复制,引用计数,引用连接,破坏性复制。
std::auto_ptr Boost职能指针,ATL框架中的智能指针。常用shared_ptr,unique_ptr,weak_ptr。
智能指针类都有一个明确的explicit构造函数,所以使用智能指针的时候,要求明确的转换,不允许不明确的转换,智能指针使用的时候,就像和指针使用一样。
shared_ptr<double> pd;
double *p_reg = new double;
pd = shared_ptr<double>(p_reg);
或者 shared_ptr<double> pshared(p_reg);
注意以下不明确的转换时不允许的:例如pd = p_reg; shared_ptr<double> pshared = p_reg;
另外注意,不使用new分配内存时,不能使用shared_ptr,auto_ptr和unique_ptr 命名空间:
每个命名空间是一个作用域,命名空间可以是不连续的,接口和实现的分离,嵌套命名空间。(命名空间可以防止重名)
头文件中一边不用using std::cout等方式,一般在哪里调用,就直接使用std::cout里面,因为这样会把大量的东西带入头文件。
命名空间的别名:例如 using namespace c = std::cout; 作为别名,可以减少命名空间的长度。 多继承与虚基类:(建议不要使用多继承,或者尽量少用多继承,一般使用单继承就可以了)
很多语言中取消了多继承,但是多继承是c++的一个很重要的功能。多继承多个父类之间用“,”隔开。
多继承中注意构造函数和析构函数的的调用顺序。特别是构造函数的初始化列表,要注意父类的构造函数列表的初始化。
例如一个构造函数: subClass(int x):y(x), baseClass(2x), base1Class(x, 2x){}
得非常注意二义性问题:一个父类A有两个子类B,C,然后这两个子类B,C又是另外一个类D的父类。
所以在构建D的时候,会构造B,然后B会调用A构造一次A,同理C构造的时候,也会构造一次A,这样就会两次构建A,产生了二义性,即两个A对象。
这里一般使用虚基类解决二义性问题:即使用虚继承,这里就是B和C虚继承A。
即 class B : virtual public A{
B(){
A(); //虚基类必须重新调用父类的构成函数
}
A(){}
}
class C :virtual public A{
C(){
A(); //虚基类必须重新调用父类的构成函数
}
},
然后D 正常继承B和C class D: public B, public C{},这样之后,构造D的时候,会构造B,和C,但是这里构造B和C的时候,不去构造A了。
只有D构造的时候,一次构造A,所以就不会产生两个A了。 特殊工具和技术:
extern “C” :
allocator 类:常用于非配固定大小的内存。例如 allocator<aClass> a; a.allocate(); 为aClass类,分配了100大小的空间。
RTTI技术:如dynamic_cast动态的类型转换,在运行时进行识别技术,可以将基类转变为子类,即可以向下转换。如果是子类赋值给基类,是可以自动进行的(向上转换时自动的)。
RTTI技术,如,typeid(aClass).name()可以获取aClass的类名。
类成员的指针:可以指向类的成员的指针,例如std:: Iten_base:: *pf = &Iten_base.isBn; 指向Iten_base的成员isBn的指针pf。也可以定义类的指向成员函数的指针。
union UTest{
char cV;
int intV;
double dV;
}; 如果 UTest ut = {'a'}; cout << ut.intV << endl;输出将是97,也就是cV的assic码值,因为这个里面的都是共有的,也就是,都是同一个值。
位域:即一个类中的成员用的位,比如 class xC{ Bit v:; Bit b:;}
volatile (易变量标识符):volatile int y; 告诉c++不要对他进行优化,因为,这个变量可能不稳定。
C++进阶 面向对象基础(三)的更多相关文章
- Python进阶---面向对象第三弹(进阶篇)
Python对象中一些方法 一.__str__ class Teacher: def __init__(self,name,age): self.name=name self.age=age self ...
- java面向对象基础(三):对象转型和多态
*/ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hl ...
- Java面向对象基础三
1.函数的重载 2.构造函数的作用 (构造函数能够重载) 1.函数名必须和类名同样 2.没有返回值 3.使用 New 来调用构造函数 4.假设类中没有构造函数,编译器会自己主动帮忙载入一个參数为空.方 ...
- [.net 面向对象程序设计进阶] (4) 正则表达式 (三) 表达式助手
[.net 面向对象程序设计进阶] (2) 正则表达式(三) 表达式助手 上面两节对正则表达式的使用及.NET下使用正则表达式作了详细说明,本节主要搜集整理了常用的正则表达式提供参考. 此外为了使用方 ...
- 【重走Android之路】【Java面向对象基础(三)】面向对象思想
[重走Android之路][基础篇(三)][Java面向对象基础]面向对象思想 1 面向对象的WWH 1.1 What--什么是面向对象 首先,要理解“对象”.在Thinkin ...
- Java第三次作业——面向对象基础(封装)
Java第三次作业--面向对象基础(封装) (一)学习总结 1.什么是面向对象的封装性,Java中是如何实现封装性的?试举例说明. 封装性 封装性是面向对象的方法所应遵循的一个重要原则,它有两个含义: ...
- 04 mysql 基础三 (进阶)
mysql 基础三 阶段一 mysql 单表查询 1.查询所有记录 select * from department; select * from student; select * from ...
- 周末班:Python基础之面向对象基础
面向对象基础 面向对象和面向过程 编程思想是什么,就是用代码解决现实生活中问题的思路. 面向过程 核心点在过程二字,过程指的是解决问题的步骤,说白了就是先做什么再干什么.这种解决问题的思路就好比是工厂 ...
- 了解JavaScript 面向对象基础 & 原型与对象
面向对象语言中的对象 老是能听到什么基于对象, 面向对象. 什么是对象, 如果有面向对象基础的人可以无视了, 下面举个简单的例子给大家讲讲面向对象中, 对象的定义, 这个是比较通用的, 不过对于JS来 ...
随机推荐
- List和Map之间的转换和关联
首先,Map.values返回的是此Map中包含的所有值的collection视图. 然后利用ArrayList的构造器ArrayList(Collection<? extends E> ...
- web页面打开本地app(判断是否安装)
在应用宝中有APP申请链接: //是否可以打开App不可以跳则到下载页 $(".downNow button").on("click",function(){ ...
- 堆外内存操作类ByteBuffer
本篇主要讲解如何使用直接内存(堆外内存),并按照下面的步骤进行说明: 1 相关背景-->读写操作-->关键属性-->读写实践-->扩展-->参考说明 希望对想使用直接内存 ...
- RNG vs EDG | SKT vs KTB [20160826]
G1 RNG:丽桑卓,古拉加斯,强行开团流. EDG:崔斯特,普朗克,伊莉斯游走,全球支援流,小规模团战能以多打少. G2 RNG:塔莉垭,纳尔,烬. EDG:雷克塞,艾克,劫,冲击后排. G3 RN ...
- JavaScript笔记基础篇(二)
基础篇主要是总结一些工作中遇到的技术问题是如何解决的,应为本人属于刚入行阶段技术并非大神如果笔记中有哪些错误,或者自己的一些想法希望大家多多交流互相学习. 1.ToFixed()函数 今天在做Birt ...
- springmvc常用注解与类型转换
springmvc常用注解与类型转换 一:前置 spring -servlet.xml 注入 <!-- 启用spring mvc 注解 --> <context:annotation ...
- 在启动dubbo框架时报错。Unable to connect to zookeeper server within timeout: 5000
这是因为zookeeper服务没有启动,所以会报错超时.只要启动zookeeper就行了. zookerper的启动很简单的,网上随便搜搜都有.
- linux上安装配置vsftpd
启动: /usr/sbin/vsftpd & 端口占用: lsof -i:21 位置: whereis vsftpd 默认配置下,匿名用户登录 vsftpd 服务后的根目录是 /var/ftp ...
- Linux_05------Linux之vim编辑器
行 * -/xxx 向后搜索 * -?xxx 向前搜索 * 命令模式下: * -h: 光标左移 * -j: 光标下移 * -k: 光标上移 * -l: 光标右移 * -ctrl+f: 向下翻页(fro ...
- compile error
stray \241 程序有非法字符,如空格,引号等,一般因为从别的地方粘贴导致这个错误.