您的位置:首页 > 编程语言 > C语言/C++

C++ Big Three

2016-02-05 15:28 357 查看
极客班干货第二周:
     死磕自己,成长大家。
[b]=========简单构造函数=========[/b]
        ​上周已经详细介绍了构造函数,这里只需要说明一点,就是要明确一下写作规范,书写构造函数时最好使用this指针,即:
this->width = width;

this->height = height;
this指针明确指出了等号左边是当前类的成员,否则写成下面代码会让人混乱。
<
4000
span style="font-size:16px;background-color:rgb(214,214,214);">width = width;

height = height;
当然你也可以定义其他变量名字,改变这种歧义,并同时进行初始化列表。
[b]=========拷贝构造函数=========[/b]
        ​首先,拷贝构造函数中出现的通病应该是没有考虑到基类的继承,既然写了Shape基类,就不会白写,应该在编程的过程中重视代码中的每一条语句,要做到不添加无用代码,也不忽视程序中出现的每一句代码。
        ​其次,拷贝构造函数中还要注意代码的鲁棒性,编写任何程序都应该注意这个问题,编写函数时要防止外部代码改变导致本函数失效或者导致程序崩溃。在本次拷贝构造函数中,你可以对leftup中的x,y分别进行赋值,但前提是在你对Point类内部完全了解的情况下进行实现的,但是如果Point类中成员发生改变,你编写的拷贝构造函数也会相应失效,所以更合理的代码是写成下列形式:
this->leftup = new Point(*(other.leftup));
        ​接下来说说拷贝构造的顺序,这也是令人容易忽略的地方,
inline

Rectangle::Rectangle(const Rectangle& other):Shape(other),width(other.width),height(other.height)
    ​    ​这里也不费口舌详细解释了,大家也应该知道了,拷贝构造的顺序与你书写的顺序没有关系,无论你写成什么顺序,编译器里已经约定好顺序,即先父类,后原类中对数据定义的顺序。因此,这里的建议就是为了阅读代码方便,书写顺序最好与拷贝构造的顺序一致。
        ​最后,说说空指针的问题,如果你要拷贝构造的other是空指针,就没必要在堆中再创建分配,只能是浪费空间。因此,拷贝构造函数如下:
inline

Rectangle::Rectangle(const Rectangle& other):Shape(other),width(other.width),height(other.height){

    if(other.leftup != nullptr){

        this->leftup = new Point(*(other.leftup));

    }

    else{

        this->leftup = nullptr;

    }

}
[b]=========拷贝赋值函数=========[/b]
        ​拷贝赋值函数首先要做的就是自检,如果本身自己赋值,直接返回。
if(this == &other)
{

    return *this;

}
        ​同时,拷贝赋值函数也同样需要考虑基类的继承,李老师讲后令我豁然开朗,应该写成这样的形式:
Shape::operator=(other);
        ​这里就是把“operator=”看做一个整体,即Shape的成员函数,然后我们直接传入参数other,这样便调用了shape的默认构造函数,对no进行的赋值操作,这样做的好处是我们完全不必管Shape内部是如何实现的,只要做我们的赋值就可以了。
        ​最后,拷贝赋值需要考虑other.leftUp指针和自身leftup指针为空的4种情况。首先我们要判断leftup是否为空,如果当前类成员leftup不为空的情况下,继续判断other.leftup是否为空,如果other.leftup不为空直接进行赋值,如果other.leftup为空,需要先delete当前类中的leftup,然后将其指向nullptr;如果当前类成员leftup为空的情况下,仍需要继续判断other.leftup是否为空,如果other.leftup不为空需要重新分配内存,并同时在堆中初始化,如果other.leftup为空,不需要任何操作。
if(leftup != nullptr)

{

    if(other.leftup != nullptr)

    {

        *leftup = *(other.leftup);

    }

    else

    {

       delete leftup;//caution: memory leak

       leftup = nullptr;

    }

}

else

{

    if(other.leftup != nullptr)

    {

        leftup = new Point(*(other.leftup));

    }

}
    ​    ​这里需要说明一点:只要代码能够表述上述意思即可,也可以先判断other.leftup,再判断leftup,效果是一样的。

[b]===========析构函数===========[/b]
        ​析构函数比较简单,就是需要注意一点:
inline

Rectangle::~Rectangle()

{

    delete leftup;

    leftup = nullptr;

}
        ​delete只是对指针的指向空间的释放,并不会改变指针的值,即指针不为空。指针的本身内容,即指向空间的地址,是没有发生变化的。同时,C++是可以delete空指针的,C++不能直接delete的是野指针,是会出问题的,所以一般指针被delete之后,最好立即赋值为空,以免被再次delete而出现问题。当指针为空指针时,没有空间可释放,也就不去释放了。有些代码表面看起来没用,但是要养成好习惯,否则bug出现的时候都不知道错误在哪里。
[b]===========补充杂谈===========[/b]
1、Singleton模式小谈:
        ​最好太依赖C++做Singleton模式,同时在讨论区内有Singleton模式多线程的实现说明如下:
        ​在单线程下,C++确保这种内置的本地static对象在首次被调用时被初始化。但是在多线程环境下,这种做法会带来不确定性,在多线程下面有几种选择:(1) 虽然是多线程,但是一个进程中一定有一个主进程并且首先被执行,可以沿用meyers的实现方式,并在主线程里面初始化所有的Instance以确保在单线程环境下的Instance。也就是说,你要在主线程启动后,首先调用雷氏A::GetInstance()的函数返回本地静态引用;(2)
可以采用所谓“lock+double check”的方法,可以写成下列形式

1. 
#include <mutex>

2. 
static A* m_pInstance = NULL;

3. 
std::mutex Mutex; // mutex for critical section

4. 
  

5. 
static A& A::GetInstance()

6. 
{

7. 
  if (m_pInstance == NULL)

8. 
      {

9. 
          std::lock_guard<std::mutex> lock(Mutex); //此处锁住临界区对象

10.          if (m_pInstance == NULL)

11.             { 

12.                   // 此处再次检查m_pInstance,并初始化

13.                   m_pInstance = new A();

14.             }

15.       }

16.       return *m_pInstance;

17.}
使用:

1. 
A anI
c7dc
nstance = A::GetInstance();

2. 
anInstance.SomeMethod();

2、Code::Blocks 12.11 error about 'nullptr':
在所有代码完成以后,出现了一个意料之外的问题,error:'nullptr' was not declared in this scope.查本来以后是codeblocks这个IDE的问题,但查询资料后,了解到应该是GNU
GCC编译器的问题,只需要在编译器敲入命令-std=gnu++0x.Code::Blocks集成了GNU GCC 编译器,具体解决方法如下:菜单栏->Settings->Compiler...,在页面中部勾选Have g++ follow the ComingC++0x ISo C++ language standard [-std=gnu++0x]。如图所示:



===========作业总结===========
回顾题目:

Rectangle 类实现构造函数,拷贝构造函数,赋值操作符,析构函数。
程序编写:

//============Rectangle.h===============

#ifndef _RECTANGLE_

#define _RECTANGLE_

// forward declaration

#include<math.h>

#include<iostream>

using namespace std;

//class declaration

class Shape

{

public:

    Shape() {no = ++cnt;}

    Shape(const Shape& other) { no= other.no; ++cnt;}

    Shape& operator=(const Shape& other) { no = other.no; return *this; }

    virtual ~Shape() {--cnt;}

private:

    int no;

    static int cnt;

};

int Shape::cnt = 0;

class Point

{

    int x;

    int y;

public:

    Point(int x=0,int y=0)

    {

        this->x = x;

        this->y = y;

    }

    int get_x() const {return x;}

    int get_y() const {return y;}

};

class Rectangle: public Shape

{

    int width;

    int height;

    Point* leftup;

public:

    Rectangle(int width,int height,int x,int y);

    Rectangle(const Rectangle& other);

    Rectangle& operator=(const Rectangle& other);

    ~Rectangle();

    int Girth() const {return (width+height)*2;}

    int Area() const {return width*height;}

    int get_width() const {return width;}

    int get_height() const {return height;}

    Point* get_leftup() const {return leftup;}

};

//class definiition

/******

*ctor

******/

inline

Rectangle::Rectangle(int width=0,int height=0,int x=0,int y=0):leftup(new Point(x,y))

{

    this->width = width;

    this->height = height;

}

/******

*dtor

******/

inline

Rectangle::~Rectangle()

{

    delete leftup;

    leftup = nullptr;

}

/******

*copy ctor

******/

inline

Rectangle::Rectangle(const Rectangle& other):Shape(other),width(other.width),height(other.height){

    if(other.leftup != nullptr){

        this->leftup = new Point(*(other.leftup));

    }

    else{

        this->leftup = nullptr;

    }

}

/******

*copy operator

******/

inline Rectangle&

Rectangle:: operator=(const Rectangle& other){

    if(this == &other){//check self assignment

        return *this;

    }

    Shape::operator=(other);

    width = other.width;

    height = other.height;

    if(leftup != nullptr)

    {

       if(other.leftup != nullptr)

        {

            *leftup = *(other.leftup);

        }

        else

        {

           delete leftup;//caution: memory leak

           leftup = nullptr;

        }

    }

    else

    {

        if(other.leftup != nullptr)

        {

            leftup = new Point(*(other.leftup));

        }

    }

    return *this;

}

//output

ostream& operator<<(ostream& os,const Rectangle& other)

{

    os<<": width("<<other.get_width()<<"),"

    <<"height("<<other.get_height()<<"),"

    <<"leftup.x("<<other.get_leftup()->get_x()<<"),"

    <<"leftup.y("<<other.get_leftup()->get_y()<<"),"<<endl

    <<"       Girth="<<other.Girth()<<","

    <<" Area="<<other.Area()<<"."<<endl;

    return os;

}

#endif // _RECTANGLE_

// ============Rectangle.cpp=============

#include "Rectangle.h"

int main()

{

    Rectangle rec0;

    cout<<"rec0()"<<rec0<<endl;

    Rectangle rec1(3,4,0,0);

    Rectangle rec2(5,6,4,4);

    cout<<"rec1()"<<rec1<<endl;

    cout<<"rec2()"<<rec2<<endl;

    cout<<"----------Copy creator rec3(rec2)---------"<<endl;

    Rectangle rec3(rec2);

    cout<<"rec3()"<<rec3<<endl;

    cout<<"----------Copy operator rec3=rec1---------"<<endl;

    rec3=rec1;

    cout<<"rec3()"<<rec3<<endl;

    cout<<"----------Copy operator rec3=rec3---------"<<endl;

    rec3=rec3;

    cout<<"rec3()"<<rec3<<endl;

}

运行结果:





题目总结:

1、在Shape基类中,静态私有数据成员需要在类外面定义初始化。同时,Shape类中的函数可以根据相应要求变化。
2、程序中验证了默认构造函数、拷贝构造、拷贝赋值以及自赋值,同时实现了周长和面积的计算。
3、三大函数分别为拷贝构造函数、拷贝赋值函数和析构函数,到此应该有一个详细的学习。李老师其实提到了很多次解耦思想,一个函数只要实现各自的功能即可,不要去操作其他函数内部的内容,不要去操作底层。尤其是团队合作的时候,定义好各自的接口,调用相应的接口,实现自己当前函数的功能,并不需要知道其他函数的内部实现,也不要干涉其他函数的功能。这样在其他代码变化时,才不会影响你的代码,即保证了程序的通用性和鲁棒性。

著作权由godfrey所有,欢迎转载,也欢迎大家指出其中的不足,共同交流进步。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: