Boolan C++面向对象高级编程(上)第二周笔记

7.Big Three:拷贝构造,拷贝赋值,析构

(1)什么时候需要自己写拷贝构造和拷贝赋值函数

当编译器提供的默认拷贝构造和拷贝赋值函数不再满足要求的时候,比方说类里面带指针,必须自己写拷贝构造和拷贝赋值函数;

String(const String& str);
String& operator=(const String& str);


如果不这么做,会怎么样?如图1所示,使用默认的拷贝构造和拷贝赋值函数,是一种浅拷贝,只会把指针拷贝过来,会造成内存泄漏,同时两个指针指向同一个地方,以后改动任何一个变量,会造成另外一个的改变。


                                                                      图 1

(2)怎么写拷贝构造和拷贝赋值函数

拷贝构造:①创造自己;②拷贝

inline
String::String(const String& str)
{
	m_data = new char[strlen(str.m_data) + 1];
	strcpy(m_data, str.m_data);
}


拷贝赋值:①delete自己;②重新创造自己;③拷贝

注意:考虑自我拷贝的情况

inline
String& String::operator =(const String& str)
{
	if (this == &str)
		return *this;

	delete[] m_data;
	m_data = new char[strlen(str.m_data) + 1];
	strcpy(m_data, str.m_data);
	return *this;
}

(3)如果class里面有指针,多半要做动态分配
做了动态分配,则在创建的对象死亡之前析构函数会被调用起来;


8.堆,栈与内存管理

Stack(堆),是存在于某作用域(scope)的一块内存空间(memory space)。例如当你调用函数,函数本身会形成一个Stack用来放置它所接受的参数,以及返回地址。

在函数本体(function body)内声明的任何变量(local object),其所使用的内存块都取自上述Stack。

Heap(堆),或者说system heap,是指由操作系统提供的一块global内存空间,程序可动态分配从某种获得若干区块。

(1)stack objects的生命期

class Complex{...}
...
{
Complex c1(1,2);
}

c1便是所谓的Stack object,其生命在作用于(scope)结束之际结束。这种作用域内的object,又称为auto object,因为它会被自动清理;


(2)static local objects的生命期

class Complex {...}
...
{
static Complex c2(1,2);
}

c2便是static object,其生命在作用域(scope)结束之后仍然存在,直到整个程序结束。


(3)global objects的生命期

class Complex {...}
...
Complex c3(1,2);

int main()
{
...
}

c3便是所谓global object,其生命在整个程序结束之后才结束。也可以把它视为一种static object,其作用域是整个程序。


(4)heap objects的生命期

class Complex {...}
...
{
Complex* p=new Complex;
...
delete p;
}

p指的便是heap object,其生命在它被delete掉之后结束。如果没有delete p;会出现内存泄漏(memory leak),因为当作业域结束,p所致的heap object仍然存在,但指针p的生命却结束了,作用域之外再也看不到p(也就没有机会delete p)。


(5)new:先分配memory,再调用ctor

绝大部分编译器对调用new,转化为三步,详见图2


                                                                                    图 2

(6)delete:先调用dtor,再释放memory


                                                                                                          图 3

(7)动态分配所得的array


                                                                              图 4

(8)array new一定要搭配array delete


                                                                              图 5


10.扩展补充:类模板,函数模板及其他

(1)static


                                                                                               图 6

类complex成员函数只有一份(不可能创建了几个对象就有几个函数),但是要处理很多对象,那就得靠this pointer来处理不同的对象。 
static的部分就和对象脱离了,它存在于内存的一部分,只有一份。 
什么时候会使用static对象呢?就是和对象无关的部分。 
如银行会有很多客户对象,但利率却不会和对象有关(大体部分来说) 
staic成员函数和一般成员函数的特征就是static成员函数没有this pointer,既然没有this pointer,那static 成员函数不能和一般的函数一样去访问处理non-static data members,那只能处理static members

例如:

class Account
{
    public:
    static double m_rate;
    static void set_rate(const double& x) {m_data = x };
};

double Account::m_rate = 8.0;   //静态数据必须要这样定义,因为脱离对象,

int main()
{
    Account::set_rate(5.0);
    Account a;
    a.set_rate(7.0);
}
调用static函数有两种方法:
①通过object调用;
②通过class name 调用;


(2)Singleton模式(把ctors放在private区)

class A
{
    public:
        static A& getInstance();
        setup() {...}
    private:
        A();
        A(const A& rhs);
        static A a; 
        ... 
};

A& A::getInstance()
{
    return a;
}
a本来就存在,和对象无关,然后不想其他人创建,那就把构造函数放在private里,那怎么取得a呢,就用个static A& getInstance()取得a,这是与外界的接口。但这不是最好的写法,因为不管你用不用,a都存在。所以更好的写法如下:

class A
{
    public:
        static A& getInstance();
        setup() {...}
    private:
        A();
        A(const A& rhs);
        ... 
};

A& A::getInstance()
{
    static A a;   //只有当有人掉用这个函数,a才会存在
    return a;
}

(3)cout


                                                                                 图 7

可以看出cout就是一种ostream,实际上是重载了<<运算符的函数,用于打印不同类型的数。


(4)class template,类模板

template<typename T>
class complex
{
    public:
        complex (T r = 0, T i = 0)
        : re (r), im(i)
        {}
        complex& operator += (const complex&);
        T real() const {return re; }
        T imag() const {return im; }
    private:
        T re, im;

        friend complex& _doapl (complex*, const complex&);
};

//调用如下
{
    complex<double> c1(2.5,1.5);
    complex<int> c2(2,6);
}

(5)function template函数模板

class stone
{
    public:
    stone(int w, int h, int we)
    : _w(w), _h(h), _weight(we)
    {}
    bool operator < (const stone& rhs) const
    { return _weight < rhs._weight; }

    private:
        int _w, _h, _weight;
};

template<class T>
inline const T& min(const T& a, const T& b)
{
    return b < a ? b : a; 
}

//使用
stone r1(2,3), r2(3,3), r3;
r3 = min(r1,r2);  //则会调用min函数,函数里面会接着调用stone::operator<函数

(6)namespace

以防和别人写的东西重名。

使用方法有两种:

①using directive

using namespace std; //把namspace空间的东西全打开
cin >> i;
cout << "hello" << endl;
②using declaration

using std::cout;
std::cin >> i;
cout << "hello" << endl;

不要在头文件中使用using namespace std;,容易造成命名空间污染;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值