本章主要講解new運算符在各種情況下的使用注意事項。
c++使用new和delete運算符來動態控制內存。
一、關於靜態成員
(1)靜態成員屬於類,不屬於對象,即在多個對象中只有一個靜態成員對象副本。
(2)靜態數據成員在類中聲明,在包含類方法的文件中初始化。但如果靜態成員是const整型類型或者枚舉類型,則可以在類聲明中初始化。
(3)靜態成員函數由於沒有this指針,因此只能訪問靜態成員。對象要通過作用域限定符來調用靜態成員方法。
在自定義類String要考慮淺拷貝與深拷貝的問題,下面對自定義類中默認方法做個分析,要對函數做深拷貝和淺拷貝的考慮。
二、關於自定義類默認生成的函數
默認構造函數、默認析構函數、拷貝構造函數、賦值運算符、地址運算符
(1)默認構造函數:
帶參數的構造函數也可以是默認構造函數,只要所有的參數都有默認值。
在構造函數中使用new分配內存,必須在相應的析構函數中使用delete來釋放內存。
(2)拷貝構造函數
拷貝函數調用的三種情況。
- 新建一個對象並初始化化同類現有對象時,會調用拷貝構造函數。
String s1(s2);
String s1 = s2;
String s1 = String(s2);
String *p2 = new String(s2);
- 當函數按值傳遞時,會調用拷貝函數
- 返回局部對象時,會調用拷貝構造函數。
由於按值傳遞都將調用拷貝構造函數,因此應該按照引用來傳遞,提高效率。
默認拷貝構造函數逐個複製數據成員,複製的是成員的值,這樣就會造成淺拷貝。
淺拷貝的指的是有複製數據成員指針的值,但沒有爲分配空間,造成兩個指針指向同一塊內存,使得析構函數
對這塊內存釋放了兩次,因此對拷貝構造函數進行重載。
(3)賦值運算符
String s1 = s2;
關於這句代碼可能有兩種使用步驟:
一是初始化,直接調用拷貝構造函數。二是先調用拷貝構造函數,然後使用賦值運算符。
在自定義類型String中,關於賦值運算符要實現深拷貝。
三、在構造函數中使用new的注意事項:
1- 如果構造函數使用new來初始化指針成員,則應該在析構函數中使用delete
2- new對於delete ,new [ ] 對應delete [ ]
3- 多個構造函數中必須使用相同的方式使用new,但可以在一個構造函數中使用new,在另一個構造函數中將指針成員置空(c++新的特性使用nullptr表示空指針)
四、有關返回對象的說明
(1)返回指向const對象的引用,(不會調用拷貝構造函數)旨在提高效率。
(2)返回指向非const對象的引用,這裏主要考慮對流運算符的重載(<< 或者 >>).
(3)返回對象,不能返回局部的引用。
(4)返回const對象,防止在if判斷中將==寫成=,改變對象的值,保護數據。
五、析構函數調用的情況
(1)對象是動態,執行完定義該對象的程序是,將調用對象的析構函數。
(2)如果對象時靜態(外部、靜態(外部)或者來着名稱空間),程序解釋時,調用析構函數
(3)如果對象時new創建的,顯式調用delete刪除對象,將調用析構函數,否則該對象的析構函數不會被調用。
六、關於placement new 運算符
palacement new
在已有的內存上分配空間,不分配空間,只是構造函數的調用。
void * operator new(size_t size,void *p)
{
return p;
}
cahr buffer[1024]
Test *p2 = new(buffer) Test(200);//operator new(sizo_t ,void *p)
cout << p2->n_ <<endl;//placement new 不分配內存 + 構造函數。
自定義類String
String.h
#ifndef _String_H_
#define _String_H_
#include <iostream>
using namespace std;
class String
{
public:
String(const char *s);
String(const String &s);
String &operator=(const String &s);
String &operator=(const char *s);
String();
~String();
char & operator[](int i);
const char & operator[](int i) const;
friend bool operator<(const String &s1,const String &s2);
friend bool operator>(const String &s1,const String &s2);
friend bool operator==(const String &s1,const String &s2);
friend ostream &operator<<(ostream &out,const String &st);
friend istream &operator>>(istream &in,String &st);
static int HowMany();
private:
char *str;
int len;
static int num_strings;
static const int MAXSIZE = 80;
};
#endif //_STRING_H_
String.cpp
#include <iostream>
#include <cstring>
#include "StringBad.h"
using namespace std;
int String::num_strings = 0;
String::String(const char *s)
{
len = strlen(s);
str = new char[len + 1];
strcpy(str,s);
num_strings++;
// cout << num_strings << ":" << str << "\"object created " << endl;
}
String::String(const String &s)
{
num_strings++;
len = s.len;
str = new char[len + 1];
strcpy(str,s.str);
// cout <<num_strings << ": \"" << str << "\" object created" << endl;
}
String & String::operator=(const String & s)
{
// cout << "operator = " << endl;
if(this == &s)
{
return *this;
}
delete [] str;
len = s.len;
str = new char[len + 1];
strcpy(str,s.str);
return *this;
}
String & String::operator=(const char *s)
{
delete [] str;
len = strlen(s);
str = new char[len + 1];
strcpy(str,s);
return *this;
}
String::String()
{
len = 4;
str = new char[1];
str[0] = '\0';
num_strings++;
// cout << num_strings << ":" << str << "\"default object created" << endl;
}
String::~String()
{
// cout << "\"" << str << "\"object deleted, ";
--num_strings;
// cout << num_strings << endl;
delete [] str;
}
bool operator<(const String &s1,const String &s2)
{
return strcmp(s1.str,s2.str) < 0;
}
bool operator>(const String &s1,const String &s2)
{
return s2 < s1;
}
bool operator==(const String &s1,const String &s2)
{
return strcmp(s1.str,s2.str) == 0;
}
char & String::operator[](int i)
{
return str[i];
}
const char & String::operator[](int i) const
{
return str[i];
}
ostream & operator<<(ostream &out,const String &st)
{
out << st.str;
return out;
}
istream &operator>>(istream &in,String &st)
{
char temp[80];
in.get(temp,80);
if(in)
{
st = temp; // operator=(const char *);
}
while(in && in.get() != '\n')
continue;
return in;
}
int String::HowMany()
{
return num_strings;
}
main.cpp
#include <iostream>
#include "StringBad.h"
using namespace std;
void callme1(String &);
void callme2(String );
int main()
{
String name;
cin >> name;
cout << name << endl;
return 0;
}
void callme1(String &rsb )
{
cout << "callme1 " << endl;
}
void callme2(String rsb)
{
cout << "callem2" << endl;
}