C++基础:类与对象(构造 析构 赋值 拷贝构造)

C++中类的构造函数与析构函数,对象的赋值与复制,拷贝构造函数等。

和上一节一样,还是用Kid类来说明

 

建立对象并初始化:Kid kid(10,"rookie_j","男");另外一种使用new运算符动态建立对象:Kid *ptr=new Kid(10,"rookie_j","男");通过指针变量ptr来访问:ptr->showKid();当我们用new建立对象时,当不再使用它时,要用delete运算符释放它:delete ptr;和不同成员函数一样,如果构造函数定义在类体内部,则作为内联函数处理;

  在声明类时,对数据成员的初始化一般在构造函数中用赋值语句进行,但C++还提供了另外一种初始化数据成员的方法——用成员初始化表来实现对数据成员的初始化。它的一般形式为:类名::构造函数名([参数表]):[(成员初始化表)];成员初始化表的形式为:成员名1(初始值1),成员名2(初始值2),成员名2(初始值2);比如:

Kid::Kid(int age,char *name,char *sex):age(age),name(name),sex(sex){};

  接下来讲一下析构函数:在我第一次在C++里看到这个名词时,感觉这个知识点很深奥,结果看了以后,其实很简单。它的作用和构造函数刚好相反,用于撤销对象,如:释放分配给对象的内存空间。析构函数和构造函数名相同,但在其前面要加~符号,析构函数没有参数,也没有返回值,且不能重载,因此一个类中只有一个析构函数。以下三种情况,当对象的生命周期结束时,析构函数会被自动调用:(1)定义了全局对象,则在程序流程离开其作用域(如:main函数结束或调用Exit)时,调用该全局对象的析构函数;(2)对象被定义在函数体里,则当这个函数执行完后,该对象释放,析构函数被自动调用;(3)若一个对象使用new运算符动态创建的,在使用delete运算符释放它时,会自动调用析构函数;

View Code 
 #include "stdafx.h"
 #include <iostream>
 
 usingnamespace std;
 
 class Kid
 {
 private:
 int age;
 char*name;
 char*sex;
 public:
         Kid(int age,char*name,char*sex);
 ~Kid();
 void showKid();
 };
 
 
 Kid::Kid(int age,char*name,char*sex)
 {
     Kid::age=age;
     Kid::name=newchar[strlen(name)];
     strcpy(Kid::name,name);
     Kid::sex=newchar[strlen(sex)];
     strcpy(Kid::sex,sex);
 }
 
 Kid::~Kid()
 {
     cout<<"dispose object kid"<<endl;
     delete []name;
     delete []sex;
 }
 
 void Kid::showKid()
 {
     cout<<"姓名:"<<name<<endl<<"年龄:"<<age<<endl<<"性别:"<<sex<<endl;
 }
 
 
 int main()
 {
     Kid kid(10,"rookie_j","男");
     kid.showKid();
 
     Kid *ptr=new Kid(10,"rookie_x","女");
     ptr->showKid();
 
     delete ptr;
 
 return0;
 }

结果:

如果没有给类定义构造函数,则编译系统自动地生成一个默认的构造函数,比如在Kid类中编译系统会为其产生一个Kid::Kid(){};构造函数,这个默认的构造函数只能给对象开辟存储空间,不能给数据成员赋值,这时数据成员的初值就是随机数。对没有定义构造函数的类,其公有数据成员可以用初始化值表进行初始化,如:

class Kid
 {
 public:
 int age;
 char*name;
 char*sex;
 };
 
 int main()
 {
 
     Kid kid={10,"Rookie_j","男"};
     cout<<"姓名:"<<kid.name<<endl<<"年龄:"<<kid.age<<endl<<"性别:"<<kid.sex<<endl;
 
 return0;
 }

但只要一个类定义了构造函数,系统将不再给它提供默认构造函数;另外还有默认的析构函数(Kid::~Kid(){})一般来说默认的析构函数就能满足要求,但对一些需要做一些内部处理的则应该显式定义析构函数。带默认参数的构造函数和之前所说的带参数的成员函数是一样的,对于构造函数的重载,在这里我就不多说了,只想强调一点,如果是无参的构造函数创建对象,应该使用"类名 对象名"的形式,而不是"类名 对象名()";

 

2.对象的赋值其实和变量的赋值差不多,也是用赋值运算符=进行的,只不过进行赋值的两个对象的类型必须相同,对象之间的赋值只是数据成员的赋值,而不对成员函数赋值;

View Code 
 #include "stdafx.h"
 #include <iostream>
 
 usingnamespace std;
 
 class Kid
 {
 private:
 int age;
 char*name;
 char*sex;
 public:
         Kid(int age,char*name,char*sex);
         Kid(){    };
 ~Kid();
 void showKid();
 };
 
 
 Kid::Kid(int age,char*name,char*sex)
 {
     Kid::age=age;
     Kid::name=newchar[strlen(name)];
     strcpy(Kid::name,name);
     Kid::sex=newchar[strlen(sex)];
     strcpy(Kid::sex,sex);
 }
 
 Kid::~Kid()
 {
     cout<<"dispose object kid"<<endl;
     delete []name;
     delete []sex;
 }
 
 void Kid::showKid()
 {
     cout<<"姓名:"<<name<<endl<<"年龄:"<<age<<endl<<"性别:"<<sex<<endl;
 }
 
 int main()
 {
     Kid kid(10,"rookie_j","男"),kid2;
     kid.showKid();
 
     kid2=kid;
     kid2.showKid();
 
 return0;
 }


 

结果:

 

拷贝构造函数是一种特殊的构造函数,其形参是类对象的引用。它主要用于在建立一个新的对象时,使用已经存在的对象去初始化这个新对象。拷贝构造函数也是构造函数,所以函数名必须与类名相同,参数只有一个就是同类对象的引用,每个类必须要有一个拷贝构造函数。如果程序员自己不定义拷贝构造函数,系统会自动产生一个默认拷贝构造函数。调用拷贝构造函数的形式有代入法:类名 对象2(对象1)和赋值法:类名 对象2=对象1;

View Code 
 #include "stdafx.h"
 #include <iostream>
 
 usingnamespace std;
 
 class Kid
 {
 private:
 int age;
 char*name;
 char*sex;
 public:
         Kid(int age,char*name,char*sex);
         Kid(const Kid &kid);
 ~Kid();
 void showKid();
 };
 
 
 Kid::Kid(int age,char*name,char*sex)
 {
     Kid::age=age;
     Kid::name=newchar[strlen(name)];
     strcpy(Kid::name,name);
     Kid::sex=newchar[strlen(sex)];
     strcpy(Kid::sex,sex);
 }
 
 Kid::Kid(const Kid &kid)
 {
     Kid::age=kid.age*2;
     Kid::name=newchar[strlen(kid.name)];
     strcpy(Kid::name,kid.name);
     Kid::sex=newchar[strlen(kid.sex)];
     strcpy(Kid::sex,kid.sex);
 }
 
 Kid::~Kid()
 {
     cout<<"dispose object kid"<<endl;
     delete []name;
     delete []sex;
 }
 
 void Kid::showKid()
 {
     cout<<"姓名:"<<name<<endl<<"年龄:"<<age<<endl<<"性别:"<<sex<<endl;
 }
 
 int main()
 {
     Kid kid(10,"rookie_j","男");
     kid.showKid();
 
     Kid kid2(kid);
     kid2.showKid();
 
     Kid kid3=kid2;
     kid3.showKid();
 
 return0;
 }


 

结果:

 

同样的默认的拷贝构造函数:复制出与源对象的数据成员的值一样的新对象。调用拷贝构造函数的3种情况:(1)Kid kid2(kid1)或Kid kid2=kid1;(2)函数的形参是类的对象:fun(Kid kid){kid.showKid();}; int main(){Kid kid(10,"Rookie_j","男");fun(kid);return 0;};(3)函数返回值为类的对象:Kid fun(){Kid kid(10,"Rookie_j","男"); return kid;} int main(){ Kid kid; kid=fun();kid.showKid();return 0;};

----------------------------------------------------------------------

已知类String的原型为:
    class String
    {
     public:
        String(const char *str = NULL); // 
普通构造函数
        String(const String &other);        // 
拷贝构造函数
        ~ String(void);                     // 
析构函数
        String & operate =(const String &other);    // 
赋值函数
     private:
        char  *m_data;                // 
用于保存字符串
    };
       
请编写String的上述4个函数。
标准答案:
 
// String
的析构函数
       String::~String(void)               // 3

{
    delete [] m_data;                      
// 
由于m_data是内部数据类型,也可以写成 delete m_data;
       }
       // String
的普通构造函数             
       String::String(const char *str)      // 6

{
    if(str==NULL)                          
    {
       m_data = new char[1];    // 
若能加 NULL 判断则更好
       *m_data = ‘\0’;                      
    }                                          
    else
    {
       int length = strlen(str);           
       m_data = new char[length+1]; // 
若能加 NULL 判断则更好      
       strcpy(m_data, str);                
    }
}   
// 
拷贝构造函数
    String::String(const String &other)   // 3

    {   
    int length = strlen(other.m_data); 
    m_data = new char[length+1];      // 
若能加 NULL 判断则更好    
    strcpy(m_data, other.m_data);         
}
// 
赋值函数
    String & String::operate =(const String &other)    // 13

    {   
       // (1) 
检查自赋值                     // 4
       if(this == &other)
           return *this;//
文章来源 草根IT(www.caogenit.com)
    
// (2) 
释放原有的内存资源            // 3
       delete [] m_data;
       
       // 
3)分配新的内存资源,并复制内容 // 3
    int length = strlen(other.m_data); 
    m_data = new char[length+1];         // 
若能加 NULL 判断则更好
       strcpy(m_data, other.m_data);
       // 
4)返回本对象的引用            // 3
       return *this;

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章