学习C++.Primer.Plus 12 类和动态内存分配
阅读目录
1.操作符重载
1.static成员的初始化
static成员是单独存储的,并不是类对象的一部分,不能在类声明中初始化。(但static的int和const枚举可以在类声明中初始化。)
静态成员在类声明中声明,在包含类方法的文件中初始化,使用作用域解析符。

#ifndef TESTCLASS_H_ #define TESTCLASS_H_ class TESTCLASS { private: static int m_numb;//静态成员 ... }; #endif
2.类中隐式的成员函数
①默认构造函数,只能有一个:
要注意的是,如果在构造函数中使用new或new[],那个析构函数中一定要统一有delete或delete[],此时,如果有的构造函数中不使用new,那么指针一定要赋值为NULL或0,此时,无论delete还是delete[]都会和空指针兼容。
public TestClass();
②或者全都有默认值的构造函数:

public: TestClass(int n=0, int x=4){};
③复制构造函数,只用于初始化过程,而不是常规的赋值过程。编译器自动生成的默认复制构造函数复制的是类中非静态的成员的值。
public: TestClass(const TestClass &);
以下将调用复制构造函数:

TestClass test(test2); TestClass test = TestClass(test2); TestClass test = test2; TestClass test = new TestClass(test2);
当函数按值传递对象、函数返回对象时都会调用复制构造函数。按值传递意味着编译器创建原始对象的复本。当编译器生成临时对象时,也将使用复制构造函数。
当类中定义了指针指向new对象时,最好自定义复制构造函数,因为默认的复制构造函数只复制指针值,不复制指针指向的new的内容。
④赋值操作符,默认生成的赋值操作符也是对成员实现挨个复制
public: TestClass & TestClass::operator= (const TestClass &);
TestClass test = test1;
这里有可能是直接使用复制构造函数来创建新对象test,也有可能是先使用复制构造函数创建一个临时对象再使用赋值操作符将临时对象的值复制到test对象中。
下面是自定义的实现:
TestClass & TestClass::operator= (const TestClass & test) { if(this == &test) return *this; ... }
⑤默认析构函数
⑥地址操作符
3.重载中括号操作符
char & TestClass::operator[] (int i) { return ...; }
调用时:
test[0] = ...;
test.operator[](0) = ...;
下面的代码将出错,原因是test是常量,方法不能保证不会修改数据。
const TestClass test(...); cout << test[0];
此时应该提供一个仅供const TestClass对象使用的operator[]版本:
const char & TestClass::operator[] (int i) const { return ...; }
4.静态成员函数
函数声明中包含static,如果函数定义是独立的,那么独立的函数定义中不能包含static。
5.重载operator+ 操作符
可以考虑重载为Const类型:
const TestClass TestClass::operator+ (const TestClass test1, const TestClass test2) { return ...; }
这么一来就能防止出现 test1 + test2 = test;这种情形。
6.静态成员独立于对象被保存,new 对象之后调用delete对象阈不会释放对象里new的内存,对象里new 的内存是在对象本身的析构函数里delete的。delete对象也不会删除静态成员。delete对象的同时会调用对象的析构函数。另,可以这么new对象:TestClass test = new TestClass;会自动调用默认构造函数。
7.delete或delete[] 释放对象所占空间时,只针对地址。也就是说delete或delete[]会删除指针指向的整块地址。
使用布局new操作符生成的对象需要显示地调用析构函数:
pc3->~TestClass();
使用布局new操作符创建的对象,应以与创建顺序相反的顺序进行删除,因为后来创建的对象有可能依赖于较早创建的对象。另外,只有这些对象都销毁后才能释放用于存储这些对象的缓冲区。
8.复习一下类的转换:
把单个值转换成类类型:
TestClass(int value);
把类转换成其它类型:
operator int();
2.队列模拟
1.堆栈和链表:
堆栈是LIFO(Last In First Out) 结构。队列是FIFO(First In First Out)结构。
链表由结点构成,每一个结点包含要保存的当前节点信息 和 指向下一个结点的指针。
2.嵌套结构和类

class Queue { private: struct Node { Item item; struct Node * next; }; enum {Q_Size = 10}; const int qsize; //... public: //... }
在类声明中声明的结构、类 或 枚举,其作用域为整个类。这种声明不会创建数据对象,而只是指定了可以在类中使用的类型。访问限制受在类中声明时的访问限制符控件。如果是公共的可以通过作用域解析符在外调用。
3.成员初始化列表

Queue:Queue(int qs) : qsize(qs), front (NULL), rear(NULL) {//...}
只有构造函数才能用成员初始化列表的方法初始化。
对于引用 和 非静态的const成员,必须采用这种方法进行初始化。因为这两种类型的成员只能在被创建时进行初始化,也就是说必须在执行构造函数之前初始化,所以只能用。。。
对于本来就是类对象的成员来说,使用成员初始化列表的效率更高。
4.伪私有方法
如果暂时不需要类的某种方法,但是以后有可能会用到时,可以定义伪私有方法。比如,现在不要Queue类的复制构造函数,但以后有可能用到,那么就会自动生成一个默认的复制构造函数,以致于复制出了不想要的结果而此时却没有发现,导致不可预期的后果。为了避免这种情况,可以先加一个私有的复制构造函数定义:下次复制时就会得到可追踪的错误

class Queue { private: Queue (const Queue & q) : qsize(0){}//伪私有方法 //... }
这种方法在定义 不允许复制的类 时也很有用。
5.随机数函数

//开始先初始化种子 std::srand(std::time(0)); //以后用的时候 int randnum = std:rand();