C++ 面向对象(二)

来源:互联网 发布:淘宝隐形眼镜是真的吗 编辑:程序博客网 时间:2024/06/04 22:48

五、拷贝构造函数

1、默认拷贝构造函数

    拷贝构造函数是一种特殊的构造函数,这个构造函数有一个参数,该参数类型是本类型的一个引用。

    一般而言,我们不需要显示的提供拷贝构造函数,编译器会自动替我们合成一个默认的拷贝构造函数。默认的拷贝构造函数只进行浅拷贝,也就是说只是简单的把成员变量的值进行copy。

    以下两种情况,如果使用默认的拷贝构造函数就会出问题。

    (1)类成员变量,涉及到动态内存分配的问题。

class Point2D{public:Point2D(){_x = new int;_y = new int;}private:int *_x;int *_y;};int _tmain(int argc, _TCHAR* argv[]){Point2D pa;Point2D pb = pa;return 0;}

    pb对象和pb对象中的_x指向同一片内存,_y也指向同一片内存,任意一个对象改变其成员变量的值都会影响到另一个对象。更严重的是,当一个对象将其delete _x, _y后,另外一个对象中的_x, _y变成了野指针。

    (2)构造函数中需要做一些特殊任务。 

class Point2D{public:Point2D(){_x = new int;_y = new int;count ++;}private:int *_x;int *_y;static int count;};int Point2D::count = 0;int _tmain(int argc, _TCHAR* argv[]){Point2D pa;Point2D pb = pa;return 0;}

    pb = pa后,count的值仍然为1,并没有将计数器自增。

2、自定义拷贝构造函数

     如果默认的拷贝构造函数不能正常工作,那我们就需要主动提供拷贝构造函数,避免编译器替我们添加默认拷贝构造函数。

3、禁止默认拷贝构造函数

     如果要禁止拷贝构造,有两种方法。

    (1)必须显示的声明拷贝构造函数,且不对这个声明进行定义,以避免编译器为我们合成默认的拷贝构造函数。另外,还需要将拷贝构造函数声明为private。如果该类的成员函数或其friend函数调用拷贝构造函数或赋值操作符函数,会出现链接报错。

    (2)让该类继承自UnCopyable

class UnCopyable{public:    UnCopyable();    ~UnCopyable();private:    UnCopyable(const UnCopyable&);    UnCopyable& operator=(const UnCopyable&);};

 六、拷贝赋值操作符

   类似于拷贝构造函数。

   拷贝赋值操作符是用一个对象去覆盖另一个已经存在对象的值。而拷贝构造函数是用一个对象去初始化另一个不存在的对象。

七、类型转换

(1)隐式转换

    隐式转换,不需要任何操作符,直接转换,前提是这两种类型必须是兼容类型,不然会编译报错;    

         int iTemp = 100;short sTemp = 1;iTemp = sTemp;

(2)显式转换

    C++是强类型语言,很多时候都必须要进行显式转换;类型转换过程中,如果存在数据的丢失,就很有可能导致程序的奔溃,所以要谨慎对待。

    C++提供了四个标准转换运算符:dynamic_cast, reinterpret_cast, static_cast, const_cast。

static_cast< type-id > ( expression )
适用场景:
a)用于相关类型之间的转换,包括子类到父类的转换,父类到子类的转换;子类指针转换成父类指针是安全的;但父类指针转换成子类指针是不安全的。(父类和子类之间的动态类型转换建议用dynamic_cast)。
b) 枚举类型与整数类型之间的转换;浮点类型与指数类型之间的转换。
c)把空指针转换成目标类型的空指针。
d)把任何类型的表达式转换成void类型。
注意:
a)static_cast没有在运行时进行类型安全检查,因此,使用前,需要我们确保这种转换是安全的。相比dynamic_cast, static_cast没有类型安全检查的开销。
b) static_cast不能去掉类型的const、volitale属性(用const_cast)。

dynamic_cast<type-id>(expression)
    该运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void*;dynamic_cast运算符可以在执行期决定真正的类型。如果downcast是安全的(也就说,如果基类指针或者引用确实指向一个派生类对象)这个运算符会传回适当转型过的指针。如果downcast不安全,这个运算符会传回空指针(也就是说,基类指针或者引用没有指向一个派生类对象)。

适用场景:
   a) dynamic_cast主要用于类层次间的上行转换和下行转换,类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的, 在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
   b) 类之间的交叉转换,结果为NULL。

reinterpret_cast<type-id> (expression)
适用场景:
    a)转换的类型必须是一个指针、引用、算术类型、函数指针或者成员指针。
    b在比特位级别上进行转换。它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原先的指针值)。但不能将非32bit的实例转成指针。
    c最普通的用途就是在函数指针类型之间进行转换。
    d)很难保证移植性。

const_cast<type_id> (expression)

    修改类型的const或volatile属性。除了const 或volatile修饰之外, type_id和expression的类型是一样的。
适用场景:
    a)常量指针被转化成非常量的指针,并且仍然指向原来的对象;

   b)常量引用被转换成非常量的引用,并且仍然指向原来的对象。

class Point2D{public:Point2D(){printf("constructor\n");}~Point2D(){printf("destructor\n");}public:int _x;int _y;};int _tmain(int argc, _TCHAR* argv[]){    const Point2D const_point2d;volatile Point2D volatile_point2d;Point2D *p2d = const_cast<Point2D *> (&const_point2d);p2d->_x = 1;   // OKp2d = &const_point2d;//error C2440: '=' : cannot convert from 'const Point2D *__w64 ' to 'Point2D *'p2d->_x = 2;const Point2D *cp2d = const_cast<Point2D *> (&volatile_point2d);cp2d->_x = 1;   // error C2166: l-value specifies const object}


 

八、static数据成员/成员函数

(1)static成员函数

    static成员函数类似于普通的函数,只是这个函数需要通过class来访问,并且受public,private等的访问限制。与普通的类成员函数不同的是,static成员函数不能访问成员变量,因为没有this指针。

(2)static成员变量

    static成员变量也受public, private等的访问限制,属于全局静态数据,注意:请不要在.h文件中,对其定义,如果头文件被包含多次,则会造成重定义。

九、const成员方法

    表示不能改变成员变量的值,不管是直接的还是间接的。

十、纯虚函数和抽象类

    如果一个函数被声明为纯虚函数,也就意味着它的实现需要子类去实现,父类不会实现此函数。含有纯虚函数的类叫做抽象类,抽象类不能实例化。


0 0
原创粉丝点击