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类型,没有默认构造函数的类类型的成员,必须再初始化列表中进行初始化。 

友元数,友元类:可以直接调用友元的的私有成员。
友元函数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;
 }

 析构函数:
 如果写了析构函数,就应该写复制构造函数,复制操作符。(三法则)

 浅复制与深复制
数据成员是指针,一定要做深复制。 如果类操作了系统的资源,只要动态的分配了资源,复制的时候,也一定要写深复制。

管理指针:(当一个类有指针成员的时候,一定要注意)
常规指针类(浅复制)避免浅复制,一般可用以下方法:智能指针类(计数类)有浅复制的特性,数据共用,但是又不会产生野指针;值型类(深复制)
自己设计智能指针类中的每个成员是私有的,内部有个指针和对应的计数器变量。

重载操作符:
关键字 operator
操作符重载:
输入输出操作符重载,特别是输入操作符重载要注意检查输入。
算术操作符重载,+一般是非成员函数,+=一般作为成员函数。

操作符重载:(转换操作符)
operator int()const。 必须是成员函数,不能指定返回值,形参表必须是空的,必须显示的的返回一个指定类型的值,通常定义为const类型,防止改变被转化对象。
class A{
public:
    A():x(3){}
    operator int()const{
        return x;
    }
private:
    int x;
}
A a;
int y = a; //这样可以直接将一个类类型自动转换为了一个int类型,然后复制给y了。

基类和派生类:
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; //这里是不能用的.
    }
}
虚函数:可以重写和不重写,纯虚函数:必须重写。

动态绑定(多态)
多态性,派生类到基类的转换,引用或指针既可以指向基类对象,也可以指向派生类对象,只有通过引用或指针调用虚函数才会发生动态绑定。 

三种继承
一般用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;
}

转换和继承:
引用转换/指针转换 对象转换 (派生类到基类) 把派生类传给基类,如果是对象传递,则,无法实现多态。
1,void (Base a){a.function();} 2, void(Base &a){a.function();} 3,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 2;} 或者thow “failed”; 等方式来跑出异常。调用的时候可以用
tyr{ isXEqual();}
catch(int e){ printf(”异常 %d\n“, 1); } 当发生异常的时候,会运行catch部分,比这种return来检查出错结果是啥,会好很多。
catch(...){printf("exeption\n");} catch所有异常用‘...’代替。
异常(2):自己创建异常类
在类中创建异常类,类名一般用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>3) throw xSize();
        if(itsSize <1) throw xName();
    }
};
调用的时候可以:
try{
    array arr(3);
    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[100000000000];
}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(100); 为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:1; Bit b:2;}
 volatile (易变量标识符):volatile int y; 告诉c++不要对他进行优化,因为,这个变量可能不稳定。
时间: 2024-10-11 00:52:49

C++进阶 面向对象基础(三)的相关文章

Python进阶---面向对象第三弹(进阶篇)

Python对象中一些方法 一.__str__ class Teacher: def __init__(self,name,age): self.name=name self.age=age self.courses=[] def teach(self): print('%s teach' %self.name) def __str__(self): return '<name:%s age:%s>' %(self.name,self.age) class Course: def __init

Java面向对象基础三

1.函数的重载 2.构造函数的作用 (构造函数可以重载) 1.函数名必须和类名相同 2.没有返回值 3.使用 New 来调用构造函数 4.如果类中没有构造函数,编译器会自动帮忙加载一个参数为空.方法体为空的构造函数 5.如果类中已经存在构造函数,编译器就不会帮忙生成构造函数了.

[.net 面向对象程序设计进阶] (4) 正则表达式 (三) 表达式助手

[.net 面向对象程序设计进阶] (2) 正则表达式(三) 表达式助手 上面两节对正则表达式的使用及.NET下使用正则表达式作了详细说明,本节主要搜集整理了常用的正则表达式提供参考. 此外为了使用方便,编写了一个<正则表达式助手>方便测试正则表达式. 1.正则表达式助手 1.1 软件概述 软件名称: <正则表达式助手> 版本: 1.0 最后更新日期: 2015/07/01 作者: YuBinfeng 运行环境: .NET Framework 4.6及以上(常用表达式功能,需要联网

【Java面向对象基础(三)】面向对象思想

[喵"的Android之路][基础篇(三)][Java面向对象基础]面向对象思想 1 面向对象的WWH 1.1 What--什么是面向对象 首先,要理解“对象”.在Thinking in Java中提到“Everything is an object”,即万物皆对象.这里的“对象”指的是任何实体或者任何可以当作实体看待的“虚幻的事物”.比如现实中的人(男人.女人.老人.小孩而...).动物(大象.狮子.猴子...).桌子.凳子.汽车.ATM机等等,也可以使看不见摸不着的空气(氧气.二氧化碳.氮气

面向对象基础进阶02

一:面向对象的三大特征 封装,继承,多态 封装 1:封装的概念及作用      *通过使用private和public修饰符来修饰类的成员(字段,属性,方法等)这样有效的使私有数据和方法不被外界访问,这种使用特性就是封装:      *一个属性,一个方法也是封装,类是对属性和方法的封装:      *封装是面向对象中的一个重要特征,面向对象的三个重要特征为:封装,继承,多态:      *作用<模块化和数据隐藏> 2:类是模版,确定的对象将会拥有的特征(属性)和行为(方法):任何对象将会属于一

04 mysql 基础三 (进阶)

mysql 基础三 阶段一 mysql 单表查询 1.查询所有记录 select * from department; ? select * from student; ? select * from student_detail; 2.查询选中列记录 select s_name from student; 3.查询指定条件下的记录 select s_name from student where s_id>2; 4.查询后为列取别名 select s_name as 姓名 from stude

C#面向对象基础(一)

面向对象这个词我很早就知道了,但具体是什么东西?什么是面向对象?一直以来都 不理解,只知道它有三大特征,即封装-继承-多态.直到学习完C#视频和看完设计模式 中面向对象基础后,对C#面向对象才有了一个大致的理解.下面这幅图是我对它的一个 宏观的认识. 一.基本知识 1.类成员 属性 C#不直接访问类的数据,通过get和set访问类中字段 方法 表示类的行为.方法可以构造,也可以重载 2.封装 每个对象都包含它能进行操作所需要的所有信息,这种特性就是封装 3.继承 如果两个对象A和B,可以描述为B

C#面向对象基础

面向对象不是取代面向过程的.类.对象."人"是类,"张三"是"人"这个类的对象.类是抽象的,对象是具体的.按钮就是类,某个按钮就是对象.对象可以叫做类的实例(Instance).字段Field(和某个对象相关的变量),字段就是类的状态(不同的对象可能不一样的状态就是字段).人这个类有姓名.年龄.身高等字段.类不占内存,对象才占内存.字段描述对象特点的数据.方法Method(函数),方法就是类能够执行的动作,比如问好.吃饭等.  类的继承,类之间可

Python学习系列(八)( 面向对象基础)

 Python学习系列(八)( 面向对象基础) Python学习系列(七)( 数据库编程) 一,面向对象 1,域:属于一个对象或类的变量.有两种类型,即实例变量—属于每个实例/类的对象:类变量—属于类本身. 2,类的方法:对象也可以使用属于类的函数来具有功能,这样的函数称之为类的方法.域和方法合称为类的属性.类使用class关键字创建,类的属性被列在一个缩进块中. 3,self:类的方法与普通的函数只有一个特别的区别----他们必须有一个额外的第一个参数名称,但是在调用的时候不能为其赋值,Pyt