C++学习 之 类中的特殊函数和this指针(笔记)

1.构造函数

  构造函数是一种特殊的函数,它在对象被创建时被调用,与类同名无返回类型,可以被重载。构造函数的可以在类内实现也可以在类外实现。

  构造函数的声明类似于下面的代码:

class Human
{
 public:
    Human();//构造函数声明
};

  构造函数在类声明中实现类似于下面的代码:

class Human
{
public:
    Human ()
    {
        //构造函数的实现部分
    }
};

  构造函数在类的声明外实现类似于下面的代码:

class Human
{
public:
    Human ();
};
Human::Human()
{
     //构造函数的实现部分
};

  使用构造函数便于我们对类内的属性进行初始化,确保已知属性中不包含随机值。构造函数也是可以重载的,这说明在调用构造函数时有时是需要提供参数的,可以在不提供参数的情况下调用的构造函数

称作默认构造函数。在调用构造函数初始化对象的属性时,又需要提供不同的参数,这时重载构造函数就可以为我们提供帮助。

  构造函数的重载类似于下面的代码:

class Human
{
private:
    string Name;
    int Age;
public:
    Human (int HumanAge)
    {
       Age=HuanAge; 
    }
    Human (string HumanName,int HumanAge)
    {
        Name=HumanName;
        Age=HumanAge;
    }
};

  其实构造函数不仅可以重载,其参数还可以带有默认值。

  例如下面这样:

class Human
{
private:
    string Name;
    int Age;
public:
    Human (int HumanAge)
    {
       Age=HuanAge; 
    }
    Human (string HumanName,int HumanAge=22)//与上一段代码的区别在于给出了HumanAge的默认值
    {
        Name=HumanName;
        Age=HumanAge;
    }
   
   /*Human (int HumanAge,string HumanName="Tom")//去掉多行注释符号后,这种重载会报错,原因在于当提供一个int型的参数调用构造函数时,系统并不知道该调用Human(int)Human(int,string HumanName="Tom")
    {
        Name=HumanName;
        Age=HumanAge;
    }*/
};

 

2.析构函数

  与析构函数一样,析构函数也看上去和类同名,只是在函数名前多了波浪号"~"。每当对象不再在作用域内或通过delete被删除,进而被销毁时都将调用析构函数。这使得析构函数是重置变量以及释放动态分配的内存和其他资源的理想场所。析构函数不能够重载每个类只能有一个析构函数,如果忘记实现析构函数,系统会自动生成一个伪析构函数,由于伪析构函数为空其不能释放动态分配的内存空间。

  析构函数的类内实现类似于下面的代码:

class Human
{
 public:
    ~Human()
   {
       //析构函数实现部分
   }

};

  析构函数的类外实现类似于下面的代码:

class Human
{
 public:
    ~Human();
};
Human::Human()
{
  //析构函数实现部分
};

  对象所在的函数已调用完毕时,系统自动执行析构函数;用new开辟了一片内存空间,delete会自动调用析构函数后释放内存;对象A是对象B的成员,B的析构函数被调用时,对象A的析构函数也会被调用。

3.复制构造函数

  3.1浅复制及其存在的问题:

    当类中包含指针成员,定义的类的对象又作为实参传递给某个函数的形参(即被复制)时,指针成员将被复制。复制后的指针成员和原对象中的指针成员指向同一个内存空间,这被称为浅复制,会威   胁程序的稳定性。

  例如下面的程序就存在问题:

#include<iostream>
#include<cstring> 
using namespace std;
void strcpy_s(char* pre, const char* next)
{
    if (next != NULL)
    {
        while ((*pre++ = *next++) != '\0');
    }
}
class MyString
{
private:
    char* Buffer;
public:
    MyString(const char *InitialInput)
    {
        if (InitialInput != NULL)
        {
            Buffer = new char[strlen(InitialInput) + 1];//Buffer指向新分配的空间
            strcpy_s(Buffer, InitialInput);
        }
        else 
            Buffer = NULL;
    }
    ~MyString()
    {
        if(Buffer!=NULL)
        { 
            cout << "析构,释放内存" << endl;
            if (Buffer != NULL)
                delete[] Buffer;
        }
    }
    int GetLength()
    {
        return strlen(Buffer);
    }
    const char* GetString()
    {
        return Buffer;
    }
};
void UseMyString(MyString Input)//调用该函数时实参对象会被复制给形参Input
{
    cout << "Buffer 字数为:" << Input.GetLength();
    cout << "Buffer 内容为:" << Input.GetString();
    return;
}
int main()
{
    MyString SayHello("Hello");
    UseMyString(SayHello);
    return 0;
}

 

  在调用UseMyString函数时,实参SayHello会被浅复制给形参Input。在传递参数的过程中,SayHello里有个成员指针Buffer,SayHello.Buffer会把地址传给Input.Buffer(这就像之前的函数的参数的传递方式一样);这样会导致它们指向同一块内存空间。当UseMyString函数调用完成时会调用析构函数释放Inpu的内存空间,然后返回主函数,当主函数执行完成后会调用析构函数释放SayHello的内存空间,这时便会出现问题,因为Input.Buffer和SayHello.Buffer指向了同一块内存空间,此时却连续执行了两次析构函数释放了同一块内存空间(程序会崩溃或无法返回正常值)。

  浅复制存在的最大问题便是会可能出现复制出的对象与原对象共用某块内存的现象,这样很可能会在释放所占内存时出问题。我们便引入复制构造函数进行深度复制解决类似问题。

  3.2 使用复制构造函数:

    复制构造函数是一种特殊的重载构造函数,我们在使用类时必须提供它。每当对象被复制其中包括把对象当作参数按值传递给函数时,编译器都将调用复制构造函数。复制构造函数接受一个以引用方   式传入的当前类的对象作为参数。这个参数是源对象的别名,我们在构造函数内使用它来复制源对象,确保对所有缓冲区进行复制。

    下面给出上面浅复制修改为深复制的代码:

 

#include<iostream>
#include<cstring>
using namespace std;
void strcpy_s(char* pre, const char* next)
{
    if (next != NULL)
    {
        while ((*pre++ = *next++) != '\0');
    }
}
class MyString
{
private:
    char* Buffer;
public:
    MyString(const char* InitialInput)
    {
        if (InitialInput != NULL)
        {
            Buffer = new char[strlen(InitialInput) + 1];
            strcpy_s(Buffer, InitialInput);
        }
        else 
            Buffer = NULL;
    }
    MyString(const MyString& CopySource)//复制构造函数
    {
        cout <<"从源对象复制:"<< endl;
        if (CopySource.Buffer != NULL)
        {
            Buffer = new char[strlen(CopySource.Buffer) + 1];
            strcpy_s(Buffer, CopySource.Buffer);
        }
        else
            Buffer = NULL;
    }
    ~MyString()
    {
        if(Buffer!=NULL)
        { 
            cout << "析构,释放内存" << endl;
            if (Buffer != NULL)
                delete[] Buffer;
        }
    }
    int GetLength()
    {
        return strlen(Buffer);
    }
    const char* GetString()
    {
        return Buffer;
    }
};
void UseMyString(MyString Input)
{
    cout << "Buffer 字数为:" << Input.GetLength()<<endl;
    cout << "Buffer 内容为:" << Input.GetString()<<endl;
    return;
}
int main()
{
    MyString SayHello("Hello");
    UseMyString(SayHello);
    return 0;
}

 

4.this指针

  在C++中,一个重要的概念是保留的关键字this,在类中,关键字this包含当前对象的地址,换句话说,其值为&object。当您在类成员方法中调用其他成员方法时,编译器将隐式地传递this指针——函数 调用中不可见的参数:

 

#include<iostream>
#include<cstring>
using namespace std;
class Human
{
private:
    int Age;
    char Name;
public:
    void SetAge(int HumansAge)
    {
        Age = HumansAge;//修改当前对象的Age值
   cout<<Age;
    }
};
int main()
{
    Human FirstMan;
    FirstMan.SetAge(21);
    return 0;
}  

 

  在上面对象FirstMan调用类中静态函数时就存在着this指针的隐式传递,即调用FirstMan(21)相当于相当于FirstMan(this,21);对对象成员Age的访问相当于this->Age=HumanAge。在整个过程中this指向当前对象,存储当前对象的地址。

 :关于this指针的一个经典回答:
  当你进入一个房子后,
  你可以看见桌子、椅子、地板等,
  但是房子你是看不到全貌了。
  对于一个类的实例来说,
  你可以看到它的成员函数、成员变量,
  但是实例本身呢?
  this是一个指针,它时时刻刻指向你这个实例本身。

 

 

  

 

  

 

  

posted @ 2019-07-28 16:58  望烟听雨寒  阅读(570)  评论(0编辑  收藏  举报