CPP 12. 类和动态内存分配 2020-11-11 浏览量 715 暂无评论 # 第12章 类和动态内存分配 ## 12.1 动态内存和类 想在类中使用new和delete运算符,将会比平时多出许多问题,在这种情况下,析构函数必不可少。 🐸小知识: 1. 静态类成员特点:无论创建了多少对象,程序都只创建一个静态类变量副本; 2. 可以在类声明之外使用单独的语句初始化静态类成员; ```cpp // stringbad.h #include #ifndef STRINGBAD_H_ #define STRINGBAD_H_ class StringBad { private: char * str; int len; static int num_strings; public: StringBad(const char * s); StringBad(); ~StringBad(); friend std::ostream & operator<<(std::ostream & os, const StringBad & st); }; #endif // stringbad.cpp #include #include "stringbad.h" using std::cout; int StringBad::num_strings = 0; StringBad::StringBad(const char * s) { len = std::strlen(s); str = new char[len + 1]; std::strcpy(str, s); num_strings++; cout << num_strings << ": \"" << str << "\" object created\n"; } StringBad::StringBad() { len = 4; str = new char[4]; std::strcpy(str, "C++"); num_strings++; cout << num_strings << ": \"" << str << "\" default object created\n"; } StringBad::~StringBad() { cout << "\"" << str "\" object deleted, "; --num_strings; cout << num_strings << " left\n"; delete [] str; } std::ostream & operator<<(std::ostream & os, const StringBad & st) { os << st.str; return os; } ``` ## 12.2 构造函数中使用new的注意事项 - 如果在构造函数中使用new来初始化指针成员,则应在析构函数中使用delete; - new和delete必须相互兼容,new对应于delete,new[]对应于delete[]; - 如果有多个构造函数,则必须以相同的方式使用new,因为析构函数只有一个; - 应定义一个复制构造函数,通过深度复制将一个对象初始化为另一个对象; ```cpp String::String(const String & st) { num_strings++; len = st.len; str = new char [len + 1]; std::strcpy(str, st.str); } ``` - 应定义一个赋值运算符,通过深度复制将一个对象复制给另一个对象; ```cpp String & String::operator=(const String & st) { if (this == &st) return *this; delete [] str; len = st.len; std::strcpy(str, st.str); return *this; } ``` 🌈小提示: 到目前为止,涉及的都是在一个类内,那如果一个类包含其他类呢?这些函数要怎么变? **第一种情况**,类中没有其他需要复制构造函数和赋值运算符的成员: ```cpp // String and string class Magazine { private: String title; string publisher; } ``` 这个例子中,不需要为Magazine类编写复制构造函数和赋值运算符,因为逐成员复制将使用成员类型定义的复制构造函数和赋值运算符。 **第二种情况**,类中有其他需要复制构造函数和赋值运算符的成员: 这些函数必须显式地调用String和string的复制构造函数和赋值运算符。 ## 12.3 有关返回对象的说明 - 返回对象将调用复制构造函数,而返回引用不会; - 如果被返回的对象是被调用函数中的局部变量,则应返回对象; - 返回const对象有助于防止将其作为左值; - 如果方法或函数要返回一个没有公有复制构造函数的类的对象,它必须返回一个纸箱这种对象的引用; ## 12.4 指针和对象 - 使用常规表示法来声明指向对象的指针; - 可以将指针初始化为指向已有对象; - 可以使用new来初始化指针,这将创建一个新的对象; - 对类使用new将调用相应的类构造函数来初始化新创建的对象; - 可以使用->运算符通过指针访问类方法; - 可以对对象指针应用解除引用运算符(*)来获得对象; - **delete不能与定位new运算符配合使用**(挺难的),需要显式地调用析构函数 ```cpp #include #include #include using namespace std; const int BUF = 512; class JustTesting { private: string words; int number; public: JustTesting(const string & s = "Just Testing", int n = 0) {words = s; number = n; cout << words << " constructed\n";} ~JustTesting() {cout << words << " destroyed\n";} void Show() const {cout << words << ", " << number << endl;} }; int main() { char * buffer = new char[BUF]; JustTesting *pc1, pc2; pc1 = new (buffer) JustTrsting; pc2 = new JustTesting("Heap1", 20); cout << "Memory block addressed:\n" << "buffer: " << (void *) buffer << " heap: " << pc2 << endl; cout << "Memory contents:\n"; cout << pc1 << ": "; pc1->Show(); cout << pc2 << ": "; pc2->Show(); JustTesting *pc3, *pc4; // 偏移量sizeof(JustTesting) pc3 = new (buffer + sizeof(JustTesting)) JustTesting("Better Idea", 6); pc4 = new JustTesting("Heap2", 10); cout << "Memory contents:\n"; cout << pc3 << ": "; pc1->Show(); cout << pc4 << ": "; pc2->Show(); delete pc2; delete pc4; pc3->~JustTesting(); pc4->~JustTesting(); delete [] buffer; cout << "Done\n"; return 0; } ``` ## 12.5 队列 🐸小知识: 1. 可以对常量进行初始化,但不能给它赋值,因此,对于const数据成员,必须在执行到构造函数体之前,即创建对象时进行初始化,C++可以用成员初始化列表完成这项工作,它并不限于初始化常量; 2. 被声明为引用的类成员同上; ```cpp // 成员初始化列表语法,这种格式只适用于构造函数 Classy::Classy(int n, int m) : mem1(n), mem2(0), mem3(n*m + 2) { //... } // 这个实现方法无法正常运行 Queue::Queue(int qs) { front = rear = NULL; item = 0; qsize = qs; } // 这个可以 Queue::Queue(int qs) : qsize(qs) { front = rear = NULL; item = 0; } ``` 先进先出(FIFO) ```cpp class Queue { private: struct Node {Item item; struct Node * next;} enum {Q_SIZE = 10}; Node * front; Node * rear; int items; const int qsize; public: Queue(int qs = Q_SIZE); ~Queue(); bool isempty() const; bool isfull() const; int queuecount() const; bool enqueue(const Item &item); bool dequeue(Item &item); }; ``` 赞赏 微信支付 支付宝支付