1. c++自動提供了以下的成員函數
默認構造函數,如果沒有定義構造函數
默認析構函數,如果沒有定義
復制構造函數,如果沒有定義,java參見:https://blog.csdn.net/ShewMi/article/details/80788591
賦值運算符,如果沒有定義
地址運算符,如果沒有定義
移動構造函數
移動賦值運算符
a. 默認構造函數
編譯器在沒有提供構造函數時候,編譯器將提供一個不接受任何參數,也不執行任何操作的構造函數
如果定義了構造函數,編譯器將不提供任何默認構造函數,如果希望創建對象時不顯示的對它進行初始化,則必須顯示定義默認構造函數,這種構造函數沒有任何參數,但是可以使用它來設置特定的值
b. 復制構造函數
復制構造函數用於將一個對象復制到一個新創建的對象中,也就是說,它用於初始化過程中,而不是常規的賦值過程中
調用的時機:
當函數的參數為類對象的時候
函數的返回值是類對象
對象需要另一個對象進行初始化
由於按值傳遞對象將調用賦值構造函數,因此應該使用按引用傳遞對象,這樣可以節省調用構造函數的時間以及存儲新對象的空間。
默認賦值構造函數的功能:逐個賦值非靜態成員(成員賦值也稱淺賦值),賦值的是成員的值,如果類中存在一個靜態的對象計數變量,默認的復制構造函數不說明其行為,也不增加計數器的個數,但是析構函數會更新次數,解決方法是提供一個對計數進行更新的顯示復制構造函數
如果對象中存在指針變量,默認的復制構造函數將不會為該指針分配空間,復制時候會將原對象和復制對象的指針指向同一個位置,析構兩個對象會對同一個指針delete兩次,從而會出現嚴重的問題。
警告:如果類中包含了使用new 初始化的指針成員,應當定義一個復制構造函數,以復制指向的數據,而不是指針,這稱為深度復制。復制的另一種形式(成員復制或者淺復制)只是復制指針值,淺復制僅淺淺復制指針的信息,而不會深入挖掘以復制指針引用的結構
賦值構造函數本質是構造函數,所以可以按照下面方式顯式調用:
c. 賦值運算符:
String knot;
String metoo = knot; // 調用的拷貝構造函數
String youtoo;
youtoo = knot; // 調用賦值運算符重載
默認的賦值運算符隱式實現也是對成員進行逐個賦值,如果成員本身就是類對象,則程序將使用這個類定義的賦值運算符來復制這個成員,靜態數據成員不受影響。
注意:由於目標對象可能以及引用了以前分配的數據,所以函數應該使用delete[] 來釋放這些數據
函數應當避免將對象賦值給自身,否則,在給對象重新賦值之前,釋放內存操作可能會刪除對象的內容。
函數返回一個指向調用對象的引用,通過返回一個對象,函數可以像賦值常規操作那樣,連續進行賦值
d. 默認析構函數:
如果類中有使用new的指針成員變量,析構函數是必不可少的,此外,析構函數一般被定義為virtual,這是為了重載以后被重載的類也能夠正常的釋放內存
例:String類
#include <iostream> using namespace std; class String { private: char* str; int len; static int num_strings; static const int CINLIM = 80; // static 修飾的常量在類中初始化 public: // constructors and other methods String(); String(const char*); String(const String&); ~String(); int length() const { return len; } // overloaded operator methods String& operator=(const String &); String& operator=(const char *); char& operator[](int); const char& operator[](int) const; // overloaded operator friends friend bool operator<(const String&, const String&); friend bool operator>(const String&, const String&); friend bool operator==(const String&, const String&); friend ostream& operator<<(ostream&, const String&); friend istream& operator>>(istream&, String&); // static function static int howMany(); };
#include "String.h" #include <cstring> #include <iostream> using namespace std; #pragma warning(disable:4996) int String::num_strings = 0; int String::howMany() { return num_strings; } String::String(const char* s) { len = strlen(s); str = new char[len + 1]; strcpy(str, s); num_strings++; } String::String() { len = 0; str = new char[1]; str[0] = '\0'; num_strings++; } String::String(const String& st) { num_strings++; len = st.len; str = new char[len + 1]; strcpy(str, st.str); } String::~String() { --num_strings; delete[] str; } String& String::operator=(const String& st) { if (this == &st) { return *this; } delete[] str; len = st.len; str = new char[len + 1]; strcpy(str, st.str); return *this; } String& String::operator=(const char* s) { delete[] str; len = strlen(s); str = new char[len + 1]; strcpy(str, s); return *this; } char& String::operator[](int i) { return str[i]; } const char& String::operator[](int i) const { return str[i]; } bool operator<(const String& str1, const String& str2) { return strcmp(str1.str, str2.str); } bool operator>(const String& str1, const String& str2) { return str2 < str1; } bool operator==(const String& str1, const String& str2) { return strcmp(str1.str, str2.str) == 0; } ostream& operator<<(ostream& os, const String& st) { os << st.str; return os; } istream& operator>>(istream& is, String& st) { char temp[String::CINLIM]; is.get(temp, String::CINLIM); if (is) st = temp; while (is && is.get() != '\n') { continue; } return is; }
測試代碼:
if(!cin || temp[0] == '0')
break;
較早的get(char*, int)在 讀取到空行后,返回的值不為false,然而,對於這些版本來說,如果讀取了一個空行,則字符串的第一個字符將會是一個空字符
if(!cin || temp[0] == '\0')
break;
如果執行了最新的c++標准,則if語句中的第一個條件將檢測到空行,第二個條件用於舊版本實現檢測空行。
2. 在構造函數中使用new時候應當注意的事項
a. 如果在構造函數中使用new來初始化指針成員,則應當在析構函數中使用delete
b. new 和delete必須互相兼容,new對應delete,new[] 對應於delete[]
c. 如果有多個構造函數,則必須以相同的方式使用New,要么都帶括號,要么都不帶,因為只有一個析構函數,所有的構造函數必須與它兼容,然而可以在一個構造函數中使用new初始化指針,另一個構造函數中將指針初始化為空,這是因為delete(無論是帶中括號還是不帶中括號)可以用於空指針。
NULL,0,還是nullptr:以前空指針可以用於0或NULL來表示,C程序員通常使用NULL而不是0,以指出這是一個指針,就像使用'\0'而不是0來表示空字符,以指出這是一個字符一樣,然而C++傳統上更喜歡使用簡單的0,而不是等價的NULL,但C++11提供了關鍵字nullptr,這將是一種更好的選擇。
d. 應當定義一個復制構造函數,通過深度復制將一個對象初始化為另一個對象。
e. 應當定義一個賦值運算符,通過深度復制將一個對象復制給另一個對象。
3. 返回對象的說明
a. 返回指向const對象的引用
使用const引用的常見原因是旨在提高效率,對於何時可以采用這種方法存在一些限制,如果函數返回傳遞給它的對象,可以通過返回引用來提高效率。
需要說明:返回對象調用復制構造函數,而返回引用不用。引用指向的對象應該在調用函數執行時存在,傳遞的對象是const,這樣返回的類型必須是const,這樣才匹配。
b. 返回指向非const對象的引用
兩種常見的返回非const對象情形:重載復制運算符以及重載與cout一起使用的<<運算符,operator=返回值用於連續賦值,operator<<用於拼接輸出
c. 返回對象:
如果返回的對象是被調用函數中的局部變量。
d. 返回const對象
4. 使用指向對象的指針
5. 隊列模擬:
#include <cstdlib> class Customer { private: long arrive; int processtime; public: Customer() { arrive = processtime = 0; } void set(long when) { processtime = rand() % 3 + 1; arrive = when; } long when() const{ return arrive; } int ptime() const { return processtime; } };
#include "Customer.h" typedef Customer Item; class Queue { private: struct Node { Item item; Node* next; }; enum { Q_SIZE = 10 }; Node* front; Node* rear; int items; // current number of items in Queue const int qsize; // maximum number of items in Queue // 防止系統產生默認的拷貝構造函數 // 聲明為私有后,調用Queue snick = nup; 以及 ss = nup系統將報錯 Queue(const Queue& q) : qsize(10) {} // const修飾的非靜態常量,必須使用初始化列表的方式初始化,同時也必須采用這種格式初始化引用數據成員 Queue& operator=(const Queue& q) { return *this; } public: Queue(int qs = Q_SIZE); ~Queue(); bool isEmpty() const; bool isFull() const; int queueCount() const; bool enqueue(const Item& item); // add item to end bool dequeue(Item& item); // remove item from front };
#include "Queue.h" Queue::Queue(int qs) : qsize(qs) { front = nullptr; rear = nullptr; items = 0; } Queue::~Queue() { Node* temp; while (front != nullptr) { temp = front; front = front->next; delete temp; } } int Queue::queueCount() const{ return items; } bool Queue::isEmpty() const { return items == 0; } bool Queue::isFull() const { return items == qsize; } bool Queue::enqueue(const Item& item) { if (isFull()) { return false; } Node* add = new Node; add->item = item; add->next = NULL; items++; if (front == nullptr) { front = add; } else { rear->next = add; } rear = add; return true; } bool Queue::dequeue(Item& item) { if (front == nullptr) { return false; } item = front->item; items--; Node* temp = front; front = front->next; delete temp; if (items == 0) { rear = nullptr; } return true; }
#include "Queue.h" const int MIN_PER_HR = 60; bool newcustomer(double x) { return (rand() * x / RAND_MAX) < 1; // rand(),RADN_MAX在cstdlib文件中 } int main() { srand(time(0)); // random initializing of rand() cout << "Case Study: Bank of Heather Automatic Teller\n"; cout << "Enter maxinm size of queue:"; int qs; cin >> qs; Queue line(qs); cout << "Enter the number of simulation hours: "; int hours; cin >> hours; long cyclelimit = MIN_PER_HR * hours; cout << "Enter the average of number of customer per hour: "; double perhour; cin >> perhour; double min_per_cust; min_per_cust = MIN_PER_HR; Item temp; long turnaways = 0; long customers = 0; long served = 0; long sum_line = 0; int wait_time = 0; long line_wait = 0; for (int cycle = 0; cycle < cyclelimit; cycle++) { if (newcustomer(min_per_cust)) { if (line.isFull()) { turnaways++; } else { customers++; temp.set(cycle); line.enqueue(temp); } } if (wait_time <= 0 && !line.isEmpty()) { line.dequeue(temp); wait_time = temp.ptime(); line_wait += cycle - temp.when(); served++; } if (wait_time > 0) { wait_time--; } sum_line += line.queueCount(); } if (customers > 0) { cout << "Customers accepted: " << customers << endl; cout << " custormer served: " << served << endl; cout << " turnaways: " << turnaways << endl; cout << "average queue size: "; cout.precision(2); cout.setf(ios_base::fixed, ios_base::floatfield); cout << (double) sum_line / cyclelimit << endl; cout << " average wait time: " << (double)line_wait / served << " minutes\n"; } else { cout << "No customers!\n"; } cout << "Done!\n"; return 0;