码迷,mamicode.com
首页 > 编程语言 > 详细

C++进阶 面向对象基础(三)

时间:2016-04-23 07:00:57      阅读:257      评论:0      收藏:0      [点我收藏+]

标签:

类的的定义:
初始化一般建议使用构造函数初始化列表形式:
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{
publicusing 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{
        publicvirtual 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++不要对他进行优化,因为,这个变量可能不稳定。

 

C++进阶 面向对象基础(三)

标签:

原文地址:http://www.cnblogs.com/hansjorn/p/5423463.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!