C++(3)this,析构,拷贝构造函数

180 阅读4分钟

this指针

this指针可以在类的成员函数和构造函数中使用,代表的是调用该函数的对象(构造的对象)的地址

在成员函数中,this指针指向调用该函数的对象
在构造函数中,this指针指向正在构造的对象

this指针的作用

可以在构造函数和成员函数中形参(局部变量)与成员变量重名,使用this指针来进行区分。
this可以组我诶函数的参数和返回值

类的前置声明:声明一个类名,类中的内容的具体实现实在之后的代码中定义 当类还未定义时需要使用该类类型就可以使用前置声明

class 类名;//前置声明

注意:类中访问成员变量时,尽量使用this指针,避免发生歧义。

析构

/*02-this指针*/
#include <iostream>
#include <cstring>

using namespace std;

//类的前置声明
class Animal;

//函数声明
void show_Animal(Animal &an);

class Animal {
public:
    //构造函数    一般用法
    Animal(string name, int age, double weight) {
        cout << "Animal()" << endl;
        //使用this指针访问同名成员变量
        this->name = name;
        this->age = age;
        this->weight = weight;
    }

    //成员函数
    void run() {
        cout << this->name << " run!" << endl;
    }

private:
    string name;//C++提供的字符串类 =
    int age;
    double weight;
};

void show_Animal(Animal &an) {
    cout << "show_Animal" << endl;
    an.run();
}

int main() {
    Animal an("元宵", 4, 11.2);
    an.run();


    return 0;
}
/*02-this指针---理清楚逻辑,不会很难的*/
#include <iostream>
#include <cstring>

using namespace std;

//类的前置声明
class Animal;

//函数声明
void show_Animal(Animal &an);

class Animal {
public:
    //构造函数
    Animal(string name, int age, double weight) {
        cout << "Animal()" << endl;
        //使用this指针访问同名成员变量
        this->name = name;
        this->age = age;
        this->weight = weight;
    }

    //成员函数
    void run() {
        cout << this->name << " run!" << endl;
    }


    Animal *show() {
        //this作为函数参数
        show_Animal(*this);  //传进去的是对象
        cout << this->name << ":" << this->age << ":" << this->weight << endl;
        //this作为返回值
        return this;
    }

private:
    string name;//C++提供的字符串类 =
    int age;
    double weight;
};

void show_Animal(Animal &an) {
    cout << "show_Animal" << endl;
    an.run();
}

int main() {
    //Animal an("元宵",4,11.2);
    //an.run();

    Animal *pa = new Animal("端午", 5, 9.4); //调用了一次构造
    cout << "-----" << endl;
    pa->show()->show()->run();
    cout << "-----" << endl;
    pa->show();

    delete pa;
    return 0;
}

函数

概念

析构函数是一个特殊的函数,函数名和类名相同,但是要在前面加~,既没有参数,也没有返回值

析构函数在对象被销毁自动调用一次

如果类中没有析构函数,编译器会生成一个什么也不做的析构函数

如果类中有析构函数,编译器不再做该动作

如果类中有类类型成员,先析构自己,在析构成员(先构造的要后析构)

class B{

};
class A{

B b;
}

//先构造B再构造A,先析构A再析构B (2)实现析构函数的情形

**如果在销毁对象的时候需要释放资源就要重写析构,比如堆内存,硬件设备,文件....
在构造函数中申请了内存(new),在析构函数中就要delete**
/*09-字符串类的静态函数*/
#include <iostream>
#include <cstring>

using namespace std;

class date {
public:

    date() {  //函数前面是不用加类型的
        cout << "date构造函数被调用了" << endl;

    }

    void getdate() {
        cout << "haha被调用了" << endl;
    }

    ~date() {  //析构要跟类名相同
        cout << "~getdate被销毁了" << endl;
    };
private:
    int year;
    int month;
    int daty;
};


int main() {

    date dt1;
    dt1.getdate();
    //析构函数会在函数结束后执行
    return 0;
}

练习: 为mystring类实现析构函数和获取指定位置字符的成员函数

/*06-字符串类的拷贝构造函数*/
#include <iostream>
#include <cstring>

using namespace std;

class mystring{
public:
    //构造函数
    mystring(const char *s=NULL)
    {
        if(!s){//没有传参数
            this->len = 10;
            this->str = new char[this->len];
            memset(this->str,0,10);
        }
        else{
            this->len = strlen(s)+1;
            this->str = new char[this->len];
            strcpy(this->str,s);
        }
    }

    //拷贝构造函数
    mystring(const mystring &s)
    {
        this->len = s.len;
        this->str = new char[this->len];
        strcpy(this->str,s.str);
    }

    //析构函数
    ~mystring()
    {
        delete[] this->str;
    }

    //打印字符串
    void show()
    {
        cout<<this->str<<endl;
    }

    //获取空间大小
    size_t get_len()
    {
        return this->len;
    }

    //获取字符串长度
    size_t get_size()
    {
        return strlen(this->str);
    }

    //获取指定位置的字符
    char at(size_t n)
    {
        if(n>=this->len){//越界
            return -1;
        }

        return this->str[n];
    }

    //修改字符串内容 健壮性
    void modify_str(const char *s)
    {
        if(!s){//为空修改为长度为10的空串
            delete[] this->str;
            this->len = 10;
            this->str = new char[this->len];
            memset(this->str,0,10);
        }
        else{//非空
            if(this->len<strlen(s)+1){//空间不够
                //调整空间
                delete[] this->str;
                this->len = strlen(s)+1;
                this->str = new char[this->len];
            }
            strcpy(this->str,s);
        }
    }

private:
    char *str;//字符串内容首地址
    size_t len;//空间大小
};


int main()
{
    //mystring str1;
    mystring str1("welcome to GEC!");
    str1.show();

    mystring str2 = str1;
    str2.show();
    str1.modify_str("byebye");
    str1.show();
    str2.show();

    cout<<str1.get_size()<<endl;

    return 0;
}

拷贝构造函数

概念

拷贝构造函数是一个特殊的构造函数,使用一个已有对象去初始化一个新建对象时,调用拷贝构造函数。

//A是类类型
A a;//构造函数
A b = a;//拷贝构造函数
A c;
c = a;//不是拷贝构造

实现语法

class A{
public:
A(...){...}//构造函数
/*拷贝构造*/
A(const A &a){...}
};

拷贝构造函数调用的时机

使用已有的对象去初始化新对象

A a;
A b = a;//拷贝构造
2.把一个对象传递给本类型的形参
3.把一个对象作为函数的返回值(编译器会优化成传递构造)

什么情况需要重写拷贝构造函数

若果一个类没有拷贝构造函数,编译器会自动生成一个按逐字节拷贝的拷贝构造函数
如果希望自定义拷贝构造的过程可以重写拷贝构造函数,其实就是对象中存在独立的内存(资源)重写拷贝构造
//默认的拷贝构造函数属于浅拷贝,为独立内存重新分配空间,并拷贝空间中的数据就叫深拷贝

拷贝构造是由一个旧对象初始的值去拷贝新对象的值

image-20220717211036921

/*09-字符串类的静态函数*/
#include <iostream>
#include <cstring>

using namespace std;

class date {
public:


    date(int year = 2022, int month = 7, int day = 9) {
        this->year = year;
        this->month = month;
        this->day = day;
    }

    void show() {
        cout << "year  " << this->year << " month  " << this->month << " day " << day << endl;

    }

    date(const date &dt) {
        cout << "  date(const date &t)" << endl;
        this->year = dt.year;
        this->month = dt.month;
        this->day = dt.day;

    }

    ~date() {  //析构要跟类名相同
        cout << "~getdate" << endl;
    };

private:
    int year;
    int month;
    int day;
};


int main() {

    date dt1(2000, 11, 20);
    dt1.show();

    date dt2(0000, 0000, 00);
    dt2 = dt1;
    dt2.show();
    //析构函数会在函数结束后执行
    return 0;

}

练习:

为mystring类直线拷贝构造函数,并提供获取字符串长度(有效字符个数)的成员函数。

/*06-字符串类的拷贝构造函数*/
#include <iostream>
#include <cstring>

using namespace std;

class mystring{
public:
    //构造函数
    mystring(const char *s=NULL)
    {
        if(!s){//没有传参数
            this->len = 10;
            this->str = new char[this->len];
            memset(this->str,0,10);
        }
        else{
            this->len = strlen(s)+1;
            this->str = new char[this->len];
            strcpy(this->str,s);
        }
    }

    //拷贝构造函数
    mystring(const mystring &s)
    {
        this->len = s.len;
        this->str = new char[this->len];
        strcpy(this->str,s.str);
    }

    //析构函数
    ~mystring()
    {
        delete[] this->str;
    }

    //打印字符串
    void show()
    {
        cout<<this->str<<endl;
    }

    //获取空间大小
    size_t get_len()
    {
        return this->len;
    }

    //获取字符串长度
    size_t get_size()
    {
        return strlen(this->str);
    }

    //获取指定位置的字符
    char at(size_t n)
    {
        if(n>=this->len){//越界
            return -1;
        }

        return this->str[n];
    }

    //修改字符串内容 健壮性
    void modify_str(const char *s)
    {
        if(!s){//为空修改为长度为10的空串
            delete[] this->str;
            this->len = 10;
            this->str = new char[this->len];
            memset(this->str,0,10);
        }
        else{//非空
            if(this->len<strlen(s)+1){//空间不够
                //调整空间
                delete[] this->str;
                this->len = strlen(s)+1;
                this->str = new char[this->len];
            }
            strcpy(this->str,s);
        }
    }

private:
    char *str;//字符串内容首地址
    size_t len;//空间大小
};


int main()
{
    //mystring str1;
    mystring str1("welcome to GEC!");
    str1.show();

    mystring str2 = str1;
    str2.show();
    str1.modify_str("byebye");
    str1.show();
    str2.show();

    cout<<str1.get_size()<<endl;

    return 0;
}

类中的特殊成员

const对象和const成员

语法

class A{
public:
    A(int n=0,int m=0):num(n),num_1(m){}
    void show(){....}
    void show()const{....}//const成员函数

private:
    const int num;//const成员变量
    int num_1;            
};

A a;
const A b;//const对象

应用

1.const成员函数和同名的非const成员函数构成重载关系
    非const对象优先调用非const函数,const对象只能调用const函数
2.const成员函数只能读取成员变量,不能修改成员变量
    如果一定要修改,必须在声明该成员变量前加mutable修饰符
//如果一个成员函数不会修改成员变量,就应该将其设计为const成员函数  

3.const成员变量和const对象都不能修改

静态(static)成员

静态成员分为静态成员变量和静态成员函数,静态成员属于类,不属于某个对象。

静态成员变量

静态成员变量在成员变量声明前加static,必须初始化,而且应该在类外初始化,默认初始化为0,类类型的静态成员变量调用默认构造函数。

静态成员函数

静态成员变量在成员函数声明前加static,静态成员函数只能访问静态成员变量,不能访问普通成员变量。

语法:

class A{
public:
    static int num;//静态成员变量    
    static void show(){......}//静态成员函数
};

//类外初始化
int A::num = xxx;

//访问静态成员
类名::静态成员;

静态成员无需通过对象来访问,可以直接通过类名访问。实现的机制是静态成员存储在独立的内存中,不能在在静态成员函数中使用this指针。所有同类型的对象访问的是同一个静态成员。

练习:

为mystring类提供一个静态成员函数,该函数返回一个指定长度的存储字符串的空间(char *)。

/*07-const对象和const成员*/
#include <iostream>
#include <cstring>

using namespace std;

class mystring {

public:
    static char *getSpace(size_t t) {
        return new char[t];

    }

private:
    size_t len;
    char *s;

};

int main() {
    char *string1 = mystring::getSpace(10);
    strcpy(string1, "helloworld");
    cout << string1 << endl;
    cout << sizeof(string1) << endl; //8
    return 0;
}

友元

友元的作用就是让类外数据突破访问权限的限制,可以将 类/函数 声明为某个类的友元,友元类和友元函数可以访问类中的所有数据,不受访问权限的限制。

友元函数

友元函数是一个全局函数,在类内将函数声明为友元,这个全局函数就可以访问类中的所有数据,语法如下:

在类内部声明:
    friend 函数声明;

友元类

有一个类A,在类A中将类B声明为友元,类B就可以访问类A中的所有数据,语法如下:

在类的内部声明:
    friend class 友元类名;

注意:友元不受访问权限的限制,可以访问类中的所有数据,破坏了类的封装属性,如非必要,不要使用。

作业:

实现以下类的构造函数(默认构造长度为10,值为0的数组),析构函数和拷贝构造函数

class MyArray{
public:

private:
    int *pdata;//数组首地址
    size_t len;//数组元素个数        
};

完成笔试题

image-20220717211056934

B D A C

  1. 解析 -- x是个对象,abc可能修改成员变量,修改了成员变量就修改了对象