類的成員變量和普通的變量一樣,從格式上基本沒多區(qū)別。
唯一需要注意是他們的責(zé)任是不同的,成員變量是對對象負(fù)責(zé)的,在類中,使用范圍由類決定,而普通變量則沒有這個說法。
類的成員函數(shù)也和普通函數(shù)一樣,都有返回值和形參。
它與普通函數(shù)的區(qū)別是:成員函數(shù)是一個類的成員,出現(xiàn)在類中,它的作用范圍由類來決定;而普通函數(shù)是獨立的,作用范圍是全局的,或位于某個命名空間內(nèi)。
這里不同變量和不同的函數(shù)我們后面會做一個系統(tǒng)的解釋和分析。
下面還是之前的例程,成員函數(shù)類內(nèi)聲明,類外定義。
ps:早期版本的C++成員變量聲明時不可以對其初始化,后期C++ 11,可以進行成員變量初始化賦值。
#include <iostream>
using namespace std;
//類通常定義在函數(shù)外面
class Person{
public:
//類包含的變量
char *name;
int age;
//類內(nèi)聲明
void say();
};
//類外定義
void Person::say(){
cout << name << "的年齡是" << age << endl;
}
int main(){
//創(chuàng)建對象
Person p;
p.name = "豆豆";
p.age = 16;
p.say();
return 0;
}
void Person::say()
::被稱為作用域運算符或作用域限定符,用來連接類名和函數(shù)名,指明當(dāng)前函數(shù)屬于哪個類,成員函數(shù)在類外定義時必須使用作用域限定符。
注意:在引入了類的類型后,我們再參數(shù)和返回值會有更多的選擇
#include <iostream>
using namespace std;
class Car{
public:
string name;
string color;
int wheel;
public:
void run();
};
void Car::run()
{
cout << color << "的" << name << "在跑..." << endl;
}
class CarFatory{
public:
string name;
string address;
string tel;
public:
Car* repair(Car *c);
};
Car* CarFatory::repair(Car *c)
{
if(c->wheel < 4){
c->wheel = 4;
cout << c->name << "車,修好了" << endl;
}
return c;
}
int main(){
Car *c = new Car();
c->name = "保時捷";
c->color = "紅色";
c->run();//車在跑
c->wheel = 3;//跑著跑著車輪子掉了了,壞了
CarFatory *f = new CarFatory();
Car *newCar = f->repair(c);//修車
cout << "車有" << newCar->wheel << "個輪子" << endl;
return 0;
}
在一個類中成員中變量和函數(shù)可以分為多種形態(tài)
我們先看下成員變量的部分
在C中我們經(jīng)常遇到這幾個混亂的變量
局部變量:在一個函數(shù)內(nèi)部定義的變量(包括函數(shù)形參)是局部變量,存儲在棧內(nèi)存,在函數(shù)結(jié)束后自動銷毀。
全局變量:在函數(shù)體外定義的變量,可以為本源文件中其它函數(shù)所公用,有效范圍為從定義變量的位置開始到本源文件結(jié)束,這種類型的變量就稱為“全局變量”。全局變量存儲在靜態(tài)存儲區(qū)域(靜態(tài)內(nèi)存)。
ps:全局變量可以被同一工程項目中其他文件用extern聲明后調(diào)用,對其每次進行修改都會被保存。
靜態(tài)變量又分為:靜態(tài)全局變量和靜態(tài)局部變量
靜態(tài)全局變量:在原先的全局變量前面加上了static進行修飾。存儲在靜態(tài)存儲區(qū)。跟全局變量最大的不同在于,靜態(tài)全局變量不能被其他源文件使用,只能被本源文件使用,對其每次進行修改都會被保存。
靜態(tài)局部變量:在原先的局部變量前面加上了static進行修飾。存儲在靜態(tài)存儲區(qū)內(nèi),等到整個程序結(jié)束才會被銷毀,但是它的作用域依然在函數(shù)體內(nèi)部。
ps:靜態(tài)局部變量一般實際中沒有太大作用,所以這里我們了解下就可以。
需要重點關(guān)注的幾個部分:
1、靜態(tài)成員變量
class Person{
public:
void show();
public:
static int height; //靜態(tài)成員變量
private:
char *name;
int age;
};
靜態(tài)成員變量屬于類,不屬于某個具體的對象,即使創(chuàng)建多個對象,也只為height分配一份內(nèi)存,所有對象使用的都是這份內(nèi)存中的數(shù)據(jù)。
當(dāng)某個對象修改了height,也會影響到其他對象。
注意:
1、靜態(tài)成員變量必須在類聲明的外部初始化,而且只能在類體外進行。
int Person::height = 0;
初始化時可以賦初值,也可以不賦值。如果不賦值,那么會被默認(rèn)初始化為 0。
全局?jǐn)?shù)據(jù)區(qū)的變量都有默認(rèn)的初始值 0,而動態(tài)數(shù)據(jù)區(qū)(堆區(qū)、棧區(qū))變量的默認(rèn)值是不確定的,一般認(rèn)為是垃圾值。
2、靜態(tài)成員變量的內(nèi)存既不是在聲明類時分配,也不是在創(chuàng)建對象時分配,而是在類外初始化時分配。
3、一個類中可以有一個或多個靜態(tài)成員變量,所有的對象都共享這些靜態(tài)成員變量。
4、靜態(tài)成員變量和普通靜態(tài)變量一樣,都在內(nèi)存分區(qū)中的全局?jǐn)?shù)據(jù)區(qū)分配內(nèi)存,程序結(jié)束時才釋放。
5、靜態(tài)成員變量不隨對象的創(chuàng)建而分配內(nèi)存,也不隨對象的銷毀而釋放內(nèi)存。而普通成員變量在對象創(chuàng)建時分配內(nèi)存,在對象銷毀時釋放內(nèi)存。
6、靜態(tài)成員變量既可以通過對象名訪問,也可以通過類名訪問。
關(guān)于靜態(tài)成員變量的訪問方式:
//通過類類訪問 static 成員變量
Person::height= 180;
//通過對象來訪問 static 成員變量
Person p;
p.height= 20;
//通過對象指針來訪問 static 成員變量
Person *p = new Student();
p->height= 190;
-----
2、靜態(tài)成員函數(shù)
C++中成員函數(shù)也是可以聲明為靜態(tài)成員函數(shù)的,靜態(tài)成員函數(shù)只能訪問靜態(tài)成員。
編譯器在編譯一個普通成員函數(shù)時,會隱式地增加一個形參 this,并把當(dāng)前對象的地址賦值給 this,所以普通成員函數(shù)只能在創(chuàng)建對象后通過對象來調(diào)用,因為它需要當(dāng)前對象的地址。而靜態(tài)成員函數(shù)可以通過類來直接調(diào)用,編譯器不會為它增加形參 this,它不需要當(dāng)前對象的地址,所以不管有沒有創(chuàng)建對象,都可以調(diào)用靜態(tài)成員函數(shù)。
靜態(tài)成員函數(shù)沒有 this 指針,無法在函數(shù)體內(nèi)部訪問某個對象,所以不能調(diào)用普通成員函數(shù),只能調(diào)用靜態(tài)成員函數(shù)。
靜態(tài)成員函數(shù)與普通成員函數(shù)的根本區(qū)別在于:普通成員函數(shù)有 this 指針,可以訪問類中的任意成員;而靜態(tài)成員函數(shù)沒有 this 指針,只能訪問靜態(tài)成員(包括靜態(tài)成員變量和靜態(tài)成員函數(shù))。
include <iostream>
using namespace std;
class Person{
public:
void show();
public: //聲明靜態(tài)成員函數(shù)
static int getAge();
static double getSalary();
private:
static int age;
static double salary;
private:
char *name;
};
int Person::age = 20;
double Person::salary = 5000.0;
void Person::show(){
cout<< name <<"的年齡是"<< age <<",工資是"<< salary <<endl;
}
//定義靜態(tài)成員函數(shù)
int Person::getAge(){
return age;
}
double Person::getSalary(){
return salary;
}
int main(){
int age = Person::getAge();
float salary = Person::getSalary();
cout<<"年齡"<< age <<"的員工工資是"<< salary <<endl;
return 0;
}
-----
3、空類的默認(rèn)成員函數(shù)
關(guān)于C++成員函數(shù)這是我們比較關(guān)注的
讓我們看一下空類中都有什么樣的成員函數(shù),編譯器會為空類提供哪些默認(rèn)成員函數(shù)?分別有什么樣的功能呢?
空類,聲明時編譯器不會生成任何成員函數(shù),對于空類,編譯器不會生成任何的成員函數(shù),只會生成1個字節(jié)的占位符。(在Linux下,是4個字節(jié))
C++空類編譯器自動生成的6個成員函數(shù):
一個缺省的構(gòu)造函數(shù)
一個拷貝構(gòu)造函數(shù)
一個析構(gòu)函數(shù)
一個賦值運算符
兩個取址運算符。
class Empty
{
public:
Empty(); //缺省構(gòu)造函數(shù)
Empty(const Empty &rhs); //拷貝構(gòu)造函數(shù)
~Empty(); //析構(gòu)函數(shù)
Empty& operator=(const Empty &rhs); //賦值運算符
Empty* operator&(); //取址運算符
const Empty* operator&() const; //取址運算符(const版本)
};
使用時的調(diào)用情況:
Empty *e = new Empty(); //缺省構(gòu)造函數(shù)
delete e; //析構(gòu)函數(shù)
Empty e1; //缺省構(gòu)造函數(shù)
Empty e2(e1); //拷貝構(gòu)造函數(shù)
e2 = e1; //賦值運算符
Empty *pe1 = &e1; //取址運算符(非const)
const Empty *pe2 = &e2; //取址運算符(const)
C++編譯器對這些函數(shù)的實現(xiàn):
inline Empty::Empty() //缺省構(gòu)造函數(shù)
{
}
inline Empty::~Empty() //析構(gòu)函數(shù)
{
}
inline Empty *Empty::operator&() //取址運算符(非const)
{
return this;
}
inline const Empty *Empty::operator&() const //取址運算符(const)
{
return this;
}
inline Empty::Empty(const Empty &rhs) //拷貝構(gòu)造函數(shù)
{
//對類的非靜態(tài)數(shù)據(jù)成員進行以"成員為單位"逐一拷貝構(gòu)造
//固定類型的對象拷貝構(gòu)造是從源對象到目標(biāo)對象的"逐位"拷貝
}
inline Empty& Empty::operator=(const Empty &rhs) //賦值運算符
{
//對類的非靜態(tài)數(shù)據(jù)成員進行以"成員為單位"逐一賦值
//固定類型的對象賦值是從源對象到目標(biāo)對象的"逐位"賦值。
}
m是類C中的一個類型為T的非靜態(tài)成員變量,若C沒有聲明拷貝構(gòu)造函數(shù)(賦值運算符), m將會通過T的拷貝構(gòu)造函數(shù)(賦值運算符)被拷貝構(gòu)造(賦值);該規(guī)則遞歸應(yīng)用到m的數(shù)據(jù)成員,直到找到一個拷貝構(gòu)造函數(shù)(賦值運算符)或固定類型(例如:int、double、指針等)為止。
這些函數(shù)我們后續(xù)會依次進行講解
----
4、構(gòu)造函數(shù)
構(gòu)造函數(shù)(Constructor): 它的名字和類名相同,沒有返回值,不需要用戶顯式調(diào)用(用戶也不能調(diào)用),而是在創(chuàng)建對象時自動執(zhí)行。
格式:
聲明:
類名(參數(shù)列表);
類外定義:
類名 :: 類名(參數(shù)列表) : 構(gòu)造函數(shù)的初始化列表{ 函數(shù)體 }
通過構(gòu)造函數(shù)可以在創(chuàng)建對象的同時,對對象的成員變量(屬性)進行初始化,這樣就簡化了創(chuàng)建對象后再賦值屬性值的過程。
include <iostream>
using namespace std;
class Student{
private:
char *name;
int age;
float score;
public:
//聲明構(gòu)造函數(shù)
Student(char *name, int age, float score);
//聲明普通成員函數(shù)
void show();
};
//定義構(gòu)造函數(shù)
Student::Student(char *name, int age, float score){
this->name = name;
this->age = age;
this->score = score;
}
//定義普通成員函數(shù)
void Student::show(){
cout<<name<<"的年齡是"<<age<<",成績是"<<score<<endl;
}
int main(){
//創(chuàng)建對象時向構(gòu)造函數(shù)傳參
Student stu("豆豆", 20, 93.0);
stu.show();
//創(chuàng)建對象時向構(gòu)造函數(shù)傳參
Student *pstu = new Student("哈哈", 21, 96.0);
pstu->show();
return 0;
}

構(gòu)造函數(shù)的一項重要功能是對成員變量進行初始化,為了達到這個目的,可以在構(gòu)造函數(shù)的函數(shù)體中對成員變量一一賦值,還可以采用初始化列表,從而使代碼更加簡潔。
include <iostream>
using namespace std;
class Student{
private:
char *name;
int age;
float score;
public:
Student(char *name, int age, float score);
void show();
};
//采用初始化列表
Student::Student(char *name, int age, float score): name(name), age(age), score(score){
//TODO:
}
void Student::show(){
cout<<name<<"的年齡是"<<age<<",成績是"<<score<<endl;
}
int main(){
Student stu("豆豆", 20, 93.0);
stu.show();
Student *pstu = new Student("哈哈", 21, 96.0);
pstu->show();
return 0;
}
注意:
1、構(gòu)造函數(shù)必須是 public 屬性的,否則創(chuàng)建對象時無法調(diào)用。
2、構(gòu)造函數(shù)沒有返回值。
3、函數(shù)體中不能有 return 語句。
4、使用構(gòu)造函數(shù)初始化列表并沒有效率上的優(yōu)勢,僅僅是書寫方便。
5、初始化列表可以用于全部成員變量,也可以只用于部分成員變量。
6、成員變量的初始化順序與初始化列表中列出的變量的順序無關(guān),它只與成員變量在類中聲明的順序有關(guān)。
#include <iostream>
using namespace std;
class Demo{
private:
int a;
int b;
public:
Demo(int b1);
void show();
};
Demo::Demo(int b1): b(b1), a(b){ }
void Demo::show(){ cout<< a <<", "<< b <<endl; }
int main(){
Demo obj(100);
obj.show();
return 0;
}

上面程序初始化列表等價于
Demo::Demo(int b1): m_b(b1), m_a(b){
a = b;
b = b1;
}
給 a 賦值時,b 還未被初始化,它的值是不確定的,所以輸出的 a 的值是一個奇怪的數(shù)字;
obj 在棧上分配內(nèi)存,成員變量的初始值是不確定的。
好像感覺初始化列表除了簡潔沒有其他作用,實則不然,初始化 const 成員變量的唯一方法就是使用初始化列表。
class Array{
private:
const int len;
int *arr;
public:
Array(int len);
};
//必須使用初始化列表來初始化 len
Array::Array(int len): len(len){
arr = new int[len];
}
默認(rèn)構(gòu)造函數(shù)
如果用戶自己沒有定義構(gòu)造函數(shù),那么編譯器會自動生成一個默認(rèn)的構(gòu)造函數(shù),只是這個構(gòu)造函數(shù)的函數(shù)體是空的,也沒有形參,也不執(zhí)行任何操作。
Student(){}
一個類必須有構(gòu)造函數(shù),要么用戶自己定義,要么編譯器自動生成。
一旦用戶自己定義了構(gòu)造函數(shù),不管有幾個,也不管形參如何,編譯器都不再自動生成。
注意:最后需要注意的一點是,調(diào)用沒有參數(shù)的構(gòu)造函數(shù)也可以省略括號。
在棧上創(chuàng)建對象可以寫作Student stu()或Student stu
在堆上創(chuàng)建對象可以寫作Student *pstu = new Student()或Student *pstu = new Student
它們一樣都會調(diào)用構(gòu)造函數(shù) Student()。
5、構(gòu)造函數(shù)的重載
說到構(gòu)造函數(shù)重載,我們就需要說一下重載的概念了。
函數(shù)重載是一種特殊情況,C++允許在同一作用域中聲明幾個類似的同名函數(shù),這些同名函數(shù)的形參列表(參數(shù)個數(shù),類型,順序)必須不同,
常用來處理實現(xiàn)功能類似數(shù)據(jù)類型不同的問題。
//全局的函數(shù)重載
int get();
int get(int a);
int get(float a);
int get(int a, int b);
class Calculate{
private:
int a;
int b;
public:
//構(gòu)造函數(shù)重載
Calculate();
Calculate(int a);
Calculate(int a, int b);
//成員函數(shù)重載
void sum();
//int sum(); //不是重載,與返回值無關(guān)
void sum(int a, int b);
void sum(int a, int b, int c);
void sum(double a, double b);
};
void Calculate::sum(){}
//int Calculate::sum(){}
void Calculate::sum(int a, int b){}
void Calculate::sum(int a, int b, int c){}
void Calculate::sum(double a, double b){}
后續(xù)我們還會繼續(xù)討論重載。
6、析構(gòu)函數(shù)
析構(gòu)函數(shù)也是一種特殊的成員函數(shù),沒有返回值,不需要程序員顯式調(diào)用,而是在銷毀對象時自動執(zhí)行。
構(gòu)造函數(shù)的名字和類名相同,而析構(gòu)函數(shù)的名字是在類名前面加一個~符號。
注意:
1、析構(gòu)函數(shù)沒有參數(shù),不能被重載
2、一個類只能有一個析構(gòu)函數(shù)。
3、如果用戶沒有定義析構(gòu)函數(shù),編譯器會自動生成一個默認(rèn)的析構(gòu)函數(shù)。
#include <iostream>
using namespace std;
/*
封裝一個數(shù)組類來看delete的作用
*/
class Array{
public:
Array(int len); //構(gòu)造函數(shù)
~Array(); //析構(gòu)函數(shù)
public:
void input(); //輸入數(shù)組元素函數(shù)
void out(); //顯示數(shù)組元素函數(shù)
private:
int* getElement(int i); //獲取第i個元素的指針
private:
const int len; //數(shù)組的長度
int *arr; //數(shù)組指針
int *p; //指向數(shù)組元素的指針
};
Array::Array(int len): len(len){ //使用初始化列表來給len賦值
if(len > 0){
arr = new int[len]; //動態(tài)內(nèi)存申請一個塊用于數(shù)組的內(nèi)存
}
else{
arr = NULL;
}
}
Array::~Array(){
delete[] arr; //釋放內(nèi)存
}
void Array::input(){
for(int i = 0; p = getElement(i); i++){
cin>>*getElement(i);
}
}
void Array::out(){
for(int i = 0; p = getElement(i); i++){
if(i == len - 1){
cout<<*getElement(i)<<endl;
}
else{
cout<<*getElement(i)<<", ";
}
}
}
int * Array::getElement(int i){
if(!arr || i < 0 || i >= len){
return NULL;
}
else{
return arr + i;
}
}
int main(){
int n;
cout<<"輸入數(shù)組的長度: ";
cin>>n;
Array *parr = new Array(n); //創(chuàng)建一個有n個元素的數(shù)組對象
//輸入數(shù)組元素
cout<<"請輸入 "<<n<<" 個元素: ";
parr->input();
//輸出數(shù)組元素
cout<<"數(shù)組內(nèi)元素是: ";
parr->out();
//刪除數(shù)組(對象)
delete parr;
return 0;
}

注意:
1、new 分配內(nèi)存時會調(diào)用構(gòu)造函數(shù)。
2、delete 釋放內(nèi)存時會調(diào)用析構(gòu)函數(shù)。
3、構(gòu)造函數(shù)和析構(gòu)函數(shù)對于類來說是不可或缺的。
析構(gòu)函數(shù)的調(diào)用時機
析構(gòu)函數(shù)在對象被銷毀時調(diào)用,而對象的銷毀時機與它所在的內(nèi)存區(qū)域有關(guān)。
在所有函數(shù)之外創(chuàng)建的對象是全局對象,它和全局變量類似,位于內(nèi)存分區(qū)中的全局?jǐn)?shù)據(jù)區(qū),程序在結(jié)束執(zhí)行時會調(diào)用這些對象的析構(gòu)函數(shù)。
在函數(shù)內(nèi)部創(chuàng)建的對象是局部對象,它和局部變量類似,位于棧區(qū),函數(shù)執(zhí)行結(jié)束時會調(diào)用這些對象的析構(gòu)函數(shù)。
new 創(chuàng)建的對象位于堆區(qū),通過 delete 刪除時才會調(diào)用析構(gòu)函數(shù);如果沒有 delete,析構(gòu)函數(shù)就不會被執(zhí)行。
#include <iostream>
#include <string>
using namespace std;
class Test{
public:
Test(string s);
~Test();
private:
string s;
};
Test::Test(string s): s(s){ cout<<this->s<<"構(gòu)造函數(shù)調(diào)用"<<endl; }
Test::~Test(){ cout<<s<<"析構(gòu)函數(shù)調(diào)用"<<endl; }
void function(){
//局部對象
Test obj1("對象1");
}
//全局對象
Test obj2("對象2");
int main(){
function();
//局部對象
Test obj3("對象3");
//new創(chuàng)建的對象
Test *pobj4 = new Test("對象4");
return 0;
}

7、拷貝構(gòu)造函數(shù)(復(fù)制構(gòu)造函數(shù))
拷貝構(gòu)造函數(shù)是一種特殊的構(gòu)造函數(shù),具有單個形參,該形參(常用const修飾)是對該類類型的引用。
當(dāng)定義一個新對象并用一個同類型的對象對它進行初始化時,將顯示使用復(fù)制構(gòu)造函數(shù)。
當(dāng)該類型的對象傳遞給函數(shù)或從函數(shù)返回該類型的對象時,將隱式調(diào)用復(fù)制構(gòu)造函數(shù)。
C++支持兩種初始化形式:
復(fù)制初始化 int a = 5;
直接初始化 int a(5);
對于其他類型沒有什么區(qū)別,對于類類型直接初始化直接調(diào)用實參匹配的構(gòu)造函數(shù),復(fù)制初始化總是調(diào)用復(fù)制構(gòu)造函數(shù),也就是說:
A x(2); //直接初始化,調(diào)用構(gòu)造函數(shù)
A y = x; //復(fù)制初始化,調(diào)用復(fù)制構(gòu)造函數(shù)
必須定義復(fù)制構(gòu)造函數(shù)的情況:
只包含類類型成員或內(nèi)置類型(但不是指針類型)成員的類,無須顯式地定義復(fù)制構(gòu)造函數(shù)也可以復(fù)制;
有的類有一個數(shù)據(jù)成員是指針,或者是有成員表示在構(gòu)造函數(shù)中分配的其他資源,這兩種情況下都必須定義復(fù)制構(gòu)造函數(shù)。
什么情況使用復(fù)制構(gòu)造函數(shù):
類的對象需要拷貝時,拷貝構(gòu)造函數(shù)將會被調(diào)用。以下情況都會調(diào)用拷貝構(gòu)造函數(shù):
(1)一個對象以值傳遞的方式傳入函數(shù)體
(2)一個對象以值傳遞的方式從函數(shù)返回
(3)一個對象需要通過另外一個對象進行初始化。
深拷貝和淺拷貝:
淺拷貝,指的是在對象復(fù)制時,只對對象中的數(shù)據(jù)成員進行簡單的賦值,默認(rèn)拷貝構(gòu)造函數(shù)執(zhí)行的也是淺拷貝。
在“深拷貝”的情況下,對于對象中動態(tài)成員,就不能僅僅簡單地賦值了,而應(yīng)該重新動態(tài)分配空間
深拷貝:如果一個類擁有資源,當(dāng)這個類的對象發(fā)生復(fù)制過程的時候,資源重新分配,這個過程就是重新動態(tài)分配空間
如果沒有自定義拷貝構(gòu)造函數(shù),則系統(tǒng)會創(chuàng)建默認(rèn)的拷貝構(gòu)造函數(shù),但系統(tǒng)創(chuàng)建的默認(rèn)復(fù)制構(gòu)造函數(shù)只會執(zhí)行“淺拷貝”,即將被拷貝對象的數(shù)據(jù)成員的值一一賦值給新創(chuàng)建的對象;
若該類的數(shù)據(jù)成員中有指針成員,則會使得新的對象的指針?biāo)赶虻牡刂放c被拷貝對象的指針?biāo)赶虻牡刂废嗤琩elete該指針時則會導(dǎo)致兩次重復(fù)delete而出錯。
下面是示例:
#include <iostream>
#include <string.h>
using namespace std;
class Person
{
public :
// 構(gòu)造函數(shù)
Person(char * pN);
// 系統(tǒng)創(chuàng)建的默認(rèn)復(fù)制構(gòu)造函數(shù),只做位模式拷貝
Person(Person & p);
~Person();
private :
char* pName;
};
Person::Person(char * pN){
cout << "構(gòu)造函數(shù)被調(diào)用"<<endl;
pName = new char[strlen(pN) + 1];
//在堆中開辟一個內(nèi)存塊存放pN所指的字符串
if(pName != NULL)
{
//如果pName不是空指針,則把形參指針pN所指的字符串復(fù)制給它
strcpy(pName ,pN);
}
}
Person::Person(Person &p){
//使兩個字符串指針指向同一地址位置
pName = p.pName;
}
Person::~Person(){
delete pName;
cout << "析構(gòu)函數(shù)被調(diào)用"<<endl;
}
int main( )
{
/*p1和p2的指針都指向了同一個地址
函數(shù)結(jié)束析構(gòu)時
同一個地址被delete兩次
*/
Person p1("豆豆");
Person p2(p1);
return 0;
}

// 下面自己設(shè)計復(fù)制構(gòu)造函數(shù),實現(xiàn)“深拷貝”,即不讓指針指向同一地址,而是重新申請一塊內(nèi)存給新的對象的指針數(shù)據(jù)成員
Person::Person(Person & p)
{
// 用運算符new為新對象的指針數(shù)據(jù)成員分配空間
pName = new char[strlen(p.pName)+ 1];
if(pName)
{
// 復(fù)制內(nèi)容
strcpy(pName ,p.pName);
}
// 則新創(chuàng)建的對象的pName與原對象chs的pName不再指向同一地址了
}