day45——面向对象特征

2.4 拷贝构造函数

1> 拷贝构造函数,是一个特殊的构造函数

2> 功能:使用一个类对象给另一个类对象初始化时,会自动调用拷贝构造函数

3> 定义格式

1、没有返回值
2、函数名与类同名
3、参数:该类的其他对象的引用
4、访问权限:一般为public
5、定义格式:
            类名 (类名 &other)

4> 调用时机:

1、使用一个类对象给另一个类对象初始化时

string s1("hello world"); //调用有参构造

string s2 = s1; //调用拷贝构造

string s3(s1); //调用拷贝构造

2、使用值传递时,实参取代形参的过程时,系统调用拷贝构造

3、使用值返回时,返回结果也是调用拷贝构造

5> 如果没有手动定义拷贝构造函数,那么系统会自动提供一个拷贝构造函数来完成对新对象中成员的初始化工作,如果手动定义了拷贝构造函数,系统就不再提供了

#include <iostream>
 
 
using namespace std;
 
 
class Stu
{
private:
    string name;
    int age;
    double score;
 
 
public:
    Stu() {cout<<"Stu::无参构造"<<endl;}          //无参构造
    Stu(string n, int a, double s):name(n),age(a),score(s) {cout<<"Stu::有参构造"<<endl;}  //有参构造
    ~Stu(){cout<<"Stu::析构函数"<<endl;}           //析构函数
    //自定义拷贝构造函数
    Stu(Stu &other):name(other.name),age(other.age),score(other.score)
    {
        cout<<"Stu::拷贝构造函数"<<endl;
    }
 
 
    //定义展示函数
    void show()
    {
        cout<<"name = "<<name<<endl;
        cout<<"age = "<<age<<endl;
        cout<<"score = "<<score<<endl;
    }
 
};
 
 
 
 
//定义函数传递对象
Stu fun(Stu s)
{
    s.show();
    cout<<"fun 函数结束"<<endl;
 
 
    return s;
}
 
 
int main()
{
    Stu s1("zhangpp", 18, 99);          //调用有参构造构造一个s1对象
    s1.show();
    cout<<"********************************************"<<endl;
 
 
    Stu s2 = s1;             //调用拷贝构造
    s2.show();
    cout<<"********************************************"<<endl;
 
 
    Stu s3(s1);              //调用拷贝构造
    s3.show();
 
 
    cout<<"********************************************"<<endl;
    //调用功能函数
    fun(s1);
    cout<<"********************************************"<<endl;
 
 
    return 0;
}

6> 深浅拷贝问题

       定义一个类时,如果没有手动提供拷贝构造函数,系统会自动提供一个拷贝构造函数,来完成对象中成员的初始化工作,这个拷贝构造函数称为浅拷贝,如果类中没有指针成员,使用浅拷贝是没有问题的。

       如果类中有指针成员,并且指向的其他空间时,使用浅拷贝就会出现多个对象中指针成员指向同一个内存空间,析构指针空间时,会造成对同一块内存空间的多次释放的情况,造成段错误(double free),此时就需要深拷贝。

       深拷贝需要程序手动定义拷贝构造函数,在拷贝构造函数的初始化列表中,给新对象的指针成员重新分配空间,将原来对象指针空间指向的内容拷贝到该空间中即可完成深拷贝。

#include <iostream>
 
using namespace std;
 
class Stu
{
private:
    string name;
    int age;
    double score;
    int *ptr;              //指针成员
 
public:
    Stu() {cout<<"Stu::无参构造"<<endl;}          //无参构造
    Stu(string n, int a, double s):name(n),age(a),score(s),ptr(new int(520)) {cout<<"Stu::有参构造"<<endl;}  //有参构造
    ~Stu(){
        delete ptr;             //释放堆区空间
        cout<<"Stu::析构函数"<<endl;
    }           //析构函数
    //自定义拷贝构造函数:深拷贝
    Stu(Stu &other):name(other.name),age(other.age),score(other.score),ptr(new int(*other.ptr))
    {
        cout<<"Stu::拷贝构造函数"<<endl;
    }
 
    //定义展示函数
    void show()
    {
        cout<<"name = "<<name<<endl;
        cout<<"age = "<<age<<endl;
        cout<<"score = "<<score<<endl;
        cout<<"*ptr = "<<*ptr<<endl;
        cout<<"ptr = "<<ptr<<endl;
    }
 
};
 
int main()
{
    Stu s1("zhangpp", 18, 99);          //调用有参构造构造一个s1对象
    s1.show();
    cout<<"********************************************"<<endl;
 
 
    Stu s2 = s1;             //调用拷贝构造
    s2.show();
    cout<<"********************************************"<<endl;
 
    return 0;
}

7> 移动构造函数

如果类中不显性定义拷贝构造函数或者移动构造函数,那么系统会自动提供这两个函数,如果程序员手动定义了其中任意一个函数,系统就两个都不提供了,如果需要使用,则需要程序员自己手动定义

移动构造函数,是将临时空间或将亡空间,托管给一个新的对象。

定义格式

1、没有返回值
2、函数名与类同名
3、参数:该类的其他对象的右值引用
4、访问权限:一般为public
5、定义格式:
            类名 (类名 &&other)

#include <iostream>
 
 
using namespace std;
 
 
class Stu
{
private:
    string name;
    int age;
    double score;
    int *ptr;              //指针成员
 
 
public:
    Stu() {cout<<"Stu::无参构造"<<endl;}          //无参构造
    Stu(string n, int a, double s):name(n),age(a),score(s),ptr(new int(520)) {cout<<"Stu::有参构造"<<endl;}  //有参构造
    ~Stu(){
        delete ptr;             //释放堆区空间
        cout<<"Stu::析构函数"<<endl;
    }           //析构函数
    //自定义拷贝构造函数:深拷贝
    Stu(Stu &other):name(other.name),age(other.age),score(other.score),ptr(new int(*other.ptr))
    {
        cout<<"Stu::拷贝构造函数"<<endl;
    }
 
 
    //自定义移动构造函数
    Stu(Stu &&other):name(other.name),age(other.age),score(other.score),ptr(new int(*other.ptr))
    {
        cout<<"Stu::移动构造函数"<<endl;
    }
 
    //定义展示函数
    void show()
    {
        cout<<"name = "<<name<<endl;
        cout<<"age = "<<age<<endl;
        cout<<"score = "<<score<<endl;
        cout<<"*ptr = "<<*ptr<<endl;
        cout<<"ptr = "<<ptr<<endl;
    }
 
};
 
int main()
{
    Stu s1("zhangpp", 18, 99);          //调用有参构造构造一个s1对象
    s1.show();
    cout<<"********************************************"<<endl;
 
 
    Stu s2 = s1;             //调用拷贝构造
    s2.show();
    cout<<"********************************************"<<endl;
 
 
    //调用移动构造函数
    Stu s3 = move(s1);
    s3.show();
    cout<<"********************************************"<<endl;
 
 
    return 0;
}

2.5 拷贝赋值函数

1> 拷贝赋值函数也称等号运算符重载

2> 功能:当使用一个类对象给另一个类对象赋值时,会自动调用拷贝赋值函数

string s1("hello world"); //有参构造

string s2 = s1; //拷贝构造

string s3; //无参构造

s3 = s1; //拷贝赋值函数

3> 定义格式

1、返回值:当前类的引用
2、函数名:operator=
3、访问权限:一般为public
4、参数:同类其他对象的引用
4、格式
        类名 & operator=(类名 & other)

4> 拷贝复制函数,如果不手动定义,系统会默认提供一个拷贝复制函数,如果手动定义了拷贝复制函数,那么系统就不会提供了。如果手动定义了移动构造函数,拷贝赋值函数不可用

5> 拷贝赋值函数也涉及深浅拷贝的问题:

定义一个类时,如果没有手动提供拷贝赋值函数,系统会自动提供一个拷贝赋值函数,来完成对象中成员的赋值工作,这个拷贝赋值函数称为浅拷贝,如果类中没有指针成员,使用浅拷贝是没有问题的。

如果类中有指针成员,并且指向的其他空间时,使用浅拷贝就会出现多个对象中指针成员指向同一个内存空间,析构指针空间时,会造成对同一块内存空间的多次释放的情况,造成段错误(double free),此时就需要深拷贝。

深拷贝需要程序手动定义拷贝赋值函数,在拷贝赋值函数的函数体内,给新对象的指针成员重新分配空间,将原来对象指针空间指向的内容拷贝到该空间中即可完成深拷贝。

#include <iostream>
 
 
using namespace std;
 
 
class Stu
{
private:
    string name;
    int age;
    double score;
    int *ptr;              //指针成员
 
 
public:
    Stu():ptr(new int(0)) {cout<<"Stu::无参构造"<<endl;}          //无参构造
    Stu(string n, int a, double s):name(n),age(a),score(s),ptr(new int(520)) {cout<<"Stu::有参构造"<<endl;}  //有参构造
    ~Stu(){
        delete ptr;             //释放堆区空间
        cout<<"Stu::析构函数"<<endl;
    }           //析构函数
    //自定义拷贝构造函数:深拷贝
    Stu(const Stu &other):name(other.name),age(other.age),score(other.score),ptr(new int(*other.ptr))
    {
        cout<<"Stu::拷贝构造函数"<<endl;
    }
 
 
    //自定义移动构造函数
    Stu(Stu &&other):name(other.name),age(other.age),score(other.score),ptr(new int(*other.ptr))
    {
        cout<<"Stu::移动构造函数"<<endl;
    }
 
 
    //自定义拷贝赋值函数
    Stu & operator=(const Stu &other)
    {
        if(this != &other)         //防止自己给自己赋值
        {
            //完成成员的赋值工作
            this->name = other.name;
            this->age = other.age;
            this->score = other.score;
            //        delete this->ptr;               //释放自己之前的空间
            //        this->ptr = new int(*other.ptr);         //深拷贝
            *this->ptr = *other.ptr;               //深拷贝
        }
 
 
        cout<<"拷贝赋值函数"<<endl;
        return *this;                //返回自身的引用
    }
 
 
 
 
    //定义展示函数
    void show()
    {
        cout<<"name = "<<name<<endl;
        cout<<"age = "<<age<<endl;
        cout<<"score = "<<score<<endl;
        cout<<"*ptr = "<<*ptr<<endl;
        cout<<"ptr = "<<ptr<<endl;
    }
 
};
 
 
int main()
{
    Stu s1("zhangpp", 18, 99);          //调用有参构造构造一个s1对象
    s1.show();
    cout<<"********************************************"<<endl;
 
 
    Stu s2 = s1;             //调用拷贝构造
    s2.show();
    cout<<"********************************************"<<endl;
 
 
    //调用移动构造函数
    Stu s3 = move(s1);
    s3.show();
    cout<<"********************************************"<<endl;
 
 
    //调用拷贝赋值函数
    Stu s4;                  //无参构造
    s4 = s1;                   //拷贝赋值函数  s4.operator=(s1)
 
    s4.show();
    cout<<"********************************************"<<endl;
 
    return 0;
}

6> 移动赋值函数

如果没有手动定义移动赋值函数,也没有手动定义任意一个特殊成员函数,此时,系统会自动提供一个移动赋值函数。如果提供了任意一个特殊成员函数,移动赋值函数就不提供了

#include <iostream>
 
 
using namespace std;
 
 
class Stu
{
private:
    string name;
    int age;
    double score;
    int *ptr;              //指针成员
public:
    Stu():ptr(new int(0)) {cout<<"Stu::无参构造"<<endl;}          //无参构造
    Stu(string n, int a, double s):name(n),age(a),score(s),ptr(new int(520)) {cout<<"Stu::有参构造"<<endl;}  //有参构造
    ~Stu(){
        delete ptr;             //释放堆区空间
        cout<<"Stu::析构函数"<<endl;
    }           //析构函数
    //自定义拷贝构造函数:深拷贝
    Stu(const Stu &other):name(other.name),age(other.age),score(other.score),ptr(new int(*other.ptr))
    {
        cout<<"Stu::拷贝构造函数"<<endl;
    }
 
 
    //自定义移动构造函数
    Stu(Stu &&other):name(other.name),age(other.age),score(other.score),ptr(new int(*other.ptr))
    {
        cout<<"Stu::移动构造函数"<<endl;
    }
 
 
    //自定义拷贝赋值函数
    Stu & operator=(const Stu &other)
    {
        if(this != &other)         //防止自己给自己赋值
        {
            //完成成员的赋值工作
            this->name = other.name;
            this->age = other.age;
            this->score = other.score;
            //        delete this->ptr;               //释放自己之前的空间
            //        this->ptr = new int(*other.ptr);         //深拷贝
            *this->ptr = *other.ptr;               //深拷贝
        }
 
 
        cout<<"拷贝赋值函数"<<endl;
        return *this;                //返回自身的引用
    }
 
 
    //定义移动赋值函数
    Stu & operator=(Stu &&other)
    {
        if(this != &other)         //防止自己给自己赋值
        {
            //完成成员的赋值工作
            this->name = other.name;
            this->age = other.age;
            this->score = other.score;
            //        delete this->ptr;               //释放自己之前的空间
            //        this->ptr = new int(*other.ptr);         //深拷贝
            *this->ptr = *other.ptr;               //深拷贝
        }
 
 
        cout<<"移动赋值函数"<<endl;
        return *this;                //返回自身的引用
    }
 
 
 
 
    //定义展示函数
    void show()
    {
        cout<<"name = "<<name<<endl;
        cout<<"age = "<<age<<endl;
        cout<<"score = "<<score<<endl;
        cout<<"*ptr = "<<*ptr<<endl;
        cout<<"ptr = "<<ptr<<endl;
    }
 
 
 
 
};
 
 
class name
{
public:
    name() {}
};
 
 
 
 
 
 
int main()
{
    Stu s1("zhangpp", 18, 99);          //调用有参构造构造一个s1对象
    s1.show();
    cout<<"********************************************"<<endl;
 
 
    Stu s2 = s1;             //调用拷贝构造
    s2.show();
    cout<<"********************************************"<<endl;
    //调用移动构造函数
    Stu s3 = move(s1);
    s3.show();
    cout<<"********************************************"<<endl;
    //调用拷贝赋值函数
    Stu s4;                  //无参构造
    s4 = s1;                   //拷贝赋值函数  s4.operator=(s1)
    s4.show();
    cout<<"********************************************"<<endl; 
    Stu s5;            //无参构造
    s5 = move(s1);     //移动赋值函数
    s5.show();
    cout<<"********************************************"<<endl;
    return 0;
}

2.6 取地址运算符重载函数

1> 功能:取得当前类对象的地址

2> 定义格式

1、返回值:当前类的指针
2、参数:无
3、权限:一般为public
4、格式:
            类名 * operator&()

3> 如果没有手动定义取地址运算符重载函数,系统会自动提供一个取地址运算符重载函数,如果手动定义了,系统就不再提供了

#include <iostream>
 
using namespace std;
 
class Stu
{
private:
    string name;
    int age;
    double score;
    int *ptr;              //指针成员
 
public:
    Stu():ptr(new int(0)) {cout<<"Stu::无参构造"<<endl;}          //无参构造
    Stu(string n, int a, double s):name(n),age(a),score(s),ptr(new int(520)) {cout<<"Stu::有参构造"<<endl;}  //有参构造
    ~Stu(){
        delete ptr;             //释放堆区空间
        cout<<"Stu::析构函数"<<endl;
    }           //析构函数
    //自定义拷贝构造函数:深拷贝
    Stu(const Stu &other):name(other.name),age(other.age),score(other.score),ptr(new int(*other.ptr))
    {
        cout<<"Stu::拷贝构造函数"<<endl;
    }
 
    //自定义移动构造函数
    Stu(Stu &&other):name(other.name),age(other.age),score(other.score),ptr(new int(*other.ptr))
    {
        cout<<"Stu::移动构造函数"<<endl;
    }
 
    //自定义拷贝赋值函数
    Stu & operator=(const Stu &other)
    {
        if(this != &other)         //防止自己给自己赋值
        {
            //完成成员的赋值工作
            this->name = other.name;
            this->age = other.age;
            this->score = other.score;
            //        delete this->ptr;               //释放自己之前的空间
            //        this->ptr = new int(*other.ptr);         //深拷贝
            *this->ptr = *other.ptr;               //深拷贝
        }
        cout<<"拷贝赋值函数"<<endl;
        return *this;                //返回自身的引用
    }
 
 
    //定义移动赋值函数
    Stu & operator=(Stu &&other)
    {
        if(this != &other)         //防止自己给自己赋值
        {
            //完成成员的赋值工作
            this->name = other.name;
            this->age = other.age;
            this->score = other.score;
            //        delete this->ptr;               //释放自己之前的空间
            //        this->ptr = new int(*other.ptr);         //深拷贝
            *this->ptr = *other.ptr;               //深拷贝
        }
 
 
        cout<<"移动赋值函数"<<endl;
        return *this;                //返回自身的引用
    }
 
 
    //自定义取地址运算符重载函数
    Stu * operator&()
    {
        cout<<"取地址运算符重载函数"<<endl;
        return this;
    }
 
    //定义展示函数
    void show()
    {
        cout<<"name = "<<name<<endl;
        cout<<"age = "<<age<<endl;
        cout<<"score = "<<score<<endl;
        cout<<"*ptr = "<<*ptr<<endl;
        cout<<"ptr = "<<ptr<<endl;
    }
 
};
 
class name
{
public:
    name() {}
};
 
int main()
{
    Stu s1("zhangpp", 18, 99);          //调用有参构造构造一个s1对象
    s1.show();
    cout<<"********************************************"<<endl;
 
 
    Stu s2 = s1;             //调用拷贝构造
    s2.show();
    cout<<"********************************************"<<endl;
 
 
    //调用移动构造函数
    Stu s3 = move(s1);
    s3.show();
    cout<<"********************************************"<<endl;
 
 
    //调用拷贝赋值函数
    Stu s4;                  //无参构造
    s4 = s1;                   //拷贝赋值函数  s4.operator=(s1)
 
 
    s4.show();
    cout<<"********************************************"<<endl;
    
    //调用移动赋值函数
    Stu s5;            //无参构造
    s5 = move(s1);     //移动赋值函数
    s5.show();
    cout<<"********************************************"<<endl;
 
 
    //取地址运算符重载函数
    &s1;
    cout<<"********************************************"<<endl;
 
    return 0;
}

2.7 一个空类中默认提供的特殊成员函数

class Temp
{
    public:
        Temp(){}                   //无参构造
        Temp(const Temp &other){}     //拷贝构造
        ~Temp(){}                        //析构函数
        Temp * operator&(){return this;}     //取地址运算符重载函数
        Temp(const Temp &&other){}     //移动构造
        Temp &operator=(const Temp& other){}      //拷贝赋值函数
        Temp &operator=(const Temp&& other){}      //移动赋值函            
};

三、匿名对象

1> 所谓匿名对象,就是没有名字的对象,生命周期只在当前语句内,所以可以理解成时一个将亡值

2> 定义格式:直接调用类的构造函数

3> 使用场景

1、使用匿名对象给一个新定义的对象进行初始化

2、使用匿名对象给数组进行初始化

3、使用匿名对象作为函数参数传递

#include <iostream>
using namespace std;
class Stu
{
private:
    string name;
    int age;
 
public:
    Stu() {cout<<"无参构造"<<endl;}          //无参构造
    Stu(string n, int a):name(n),age(a){cout<<"有参构造"<<endl;}    //有参构造
    ~Stu(){cout<<"析构函数"<<endl;}            //析构函数
    Stu(Stu &other):name(other.name), age(other.age){cout<<"拷贝构造"<<endl;}    //拷贝构造
    Stu(Stu &&other):name(other.name), age(other.age){cout<<"移动构造"<<endl;}    //移动构造
 
 
    void show()
    {
        cout<<"name = "<<name<<"    age = "<<age<<endl;
    }
};
 
 
//自定义全局函数
void fun(Stu s)
{
    s.show();
}
 
 
int main()
{
    Stu s1("zhangpp", 18);        //调用有参构造
    s1.show();
    cout<<"***********************************"<<endl;
 
    Stu("lisi", 20);             //定义一个匿名对象
    cout<<"***********************************"<<endl;
 
    //匿名对象使用方式1:给新的对象进行初始化工作
    Stu s2 = Stu("wangwu", 30);           //依赖移动构造
 
    cout<<"***********************************"<<endl;
    //匿名对象使用方式2:给对象数组进行初始化
    Stu s[3] = {Stu("zhaoliu", 30), Stu("sunqi", 30), Stu("zhouri", 30)};
    cout<<"***********************************"<<endl;
 
    //匿名对象使用方式3:作为函数的形参进行传递
    fun(Stu("wukong", 500));
    cout<<"***********************************"<<endl;
 
    return 0;
}

四、友元

4.1 友元的引入

由于类的封装性较强,对类外的所有数据都屏蔽了信息,尤其是私有成员。但是,程序员有时候又想要在外界某个函数或某个类中访问其他类的私有成员,此时就可以引入友元,在某个类中,将外部的信息设置成友元,那么,该友元就会无条件访问该类中的所有权限下的成员。

4.2 友元的分类

友元分为友元函数和友元类

4.3 友元函数

1> 全局函数作为友元函数

声明类外的一个全局函数当作该类的友元函数,那么该函数就能无条件的访问该类中的任意权限下的成员

声明格式: friend 函数头;

#include <iostream>
 
 
using namespace std;
class Stu;                  //对类的前置声明
void  fun(Stu s);
 
 
 
 
class Stu
{
private:
    string name;
    int age;
 
 
public:
    Stu() {cout<<"无参构造"<<endl;}          //无参构造
    Stu(string n, int a):name(n),age(a){cout<<"有参构造"<<endl;}    //有参构造
    ~Stu(){cout<<"析构函数"<<endl;}            //析构函数
    Stu(Stu &other):name(other.name), age(other.age){cout<<"拷贝构造"<<endl;}    //拷贝构造
    Stu(Stu &&other):name(other.name), age(other.age){cout<<"移动构造"<<endl;}    //移动构造
 
 
    void show()
    {
        cout<<"name = "<<name<<"    age = "<<age<<endl;
    }
 
 
    //声明一个全局函数作为友元函数
    friend void  fun(Stu s);
};
 
 
//定义一个全局函数
void  fun(Stu s)
{
    s.show();           //在类外可以正常调用类中的公共权限下的成员
    cout<<"name = "<<s.name<<endl;       //友元函数可以访问对应类中的所有权限下的成员
    cout<<"age = "<<s.age<<endl;        //友元函数可以访问对应类中的所有权限下的成员
}
 
 
 
 
 
 
int main()
{
    Stu s1("zhangsan", 18);
    fun(s1);
 
 
    return 0;
}

练习:

分别定义两个类:猫(Cat)和狗(Dog),分别有私有成员:总个数(count)、总重量(weight)

成员函数:有参构造函数、析构函数

定义全局函数:在全局函数中,传入一个猫类对象和一个狗类对象,求猫和狗的总个数和总重量并输出

#include <iostream>
 
 
using namespace std;
class Cat;                  //声明猫类
class Dog;                  //声明狗类
void fun(Cat a,Dog b);      //声明全局函数
 
 
class Cat
{
private:
    int count;
    double weight;
 
 
 
 
public:
    //有参构造函数
    Cat(int c ,double w):count(c),weight(w)
    {
 
 
    }
    //析构函数
    ~Cat()
    {
 
 
    }
    void show()
    {
        cout<<"猫个数:"<<count<<endl;
        cout<<"猫重量:"<<weight<<endl;
    }
    friend void fun(Cat a,Dog b);            //声明友元函数
};
 
 
class Dog
{
private:
    int count;
    double weight;
 
public:
    //有参构造函数
    Dog(int c ,double w):count(c),weight(w)
    {
 
 
    }
    //析构函数
    ~Dog()
    {
 
 
    }
    void show()
    {
        cout<<"狗个数:"<<count<<endl;
        cout<<"狗重量:"<<weight<<endl;
    }
    friend void fun(Cat a,Dog b);              //声明友元函数
};
 
//定义全局函数
void fun(Cat a,Dog b )
{
    a.show();
    b.show();
    cout<<"总个数="<<a.count+b.count<<endl;
    cout<<"总重量="<<a.weight+b.weight<<endl;
}
 
 
/**************************主程序********************/
int main()
{
    Cat c1(2, 5);
    Dog d1(3,6);
 
 
    fun(c1,d1);
    return 0;
}

2> 其他类的成员函数作为友元函数(了解即可)

注意:如果类中的某个函数,在其他类中设置成友元,那么该函数必须是类内声明,类外定义

4.4 友元类

声明一个A类为B类的友元类,则B允许A访问其所有权限下的成员包括私有成员

声明格式:friend 类名;

#include <iostream>
 
 
using namespace std;
class Stu;                  //对类的前置声明
void  fun(Stu s);
class Teacher;           //将老师类前置声明
 
 
//定义老师类
class Teacher
{
private:
    string name;
    string subject;          //课程
public:
    Teacher() {}
    Teacher(string n, string s):name(n), subject(s) {}
    ~Teacher(){}
 
 
    //定义成员函数
    void display(Stu s);          //只能类内声明,类外定义
 
 
};
 
 
 
 
 
 
class Stu
{
private:
    string name;
    int age;
 
 
public:
    Stu() {cout<<"无参构造"<<endl;}          //无参构造
    Stu(string n, int a):name(n),age(a){cout<<"有参构造"<<endl;}    //有参构造
    ~Stu(){cout<<"析构函数"<<endl;}            //析构函数
    Stu(Stu &other):name(other.name), age(other.age){cout<<"拷贝构造"<<endl;}    //拷贝构造
    Stu(Stu &&other):name(other.name), age(other.age){cout<<"移动构造"<<endl;}    //移动构造
 
 
    void show()
    {
        cout<<"name = "<<name<<"    age = "<<age<<endl;
    }
 
 
    //声明一个全局函数作为友元函数
    friend void  fun(Stu s);
 
 
    //声明其他类中的某个成员函数作为友元函数
    //friend void Teacher::display(Stu s);
 
 
    //声明老师类为友元类
    friend class Teacher;
};
 
 
//将teacher类中国的函数类外定义
void Teacher::display(Stu s)
{
    cout<<"stu::name = "<<s.name<<endl;        //该函数被stu类声明为友元函数
    cout<<"stu::age = "<<s.age<<endl;
}
 
 
 
 
 
 
//定义一个全局函数
void  fun(Stu s)
{
    s.show();           //在类外可以正常调用类中的公共权限下的成员
    cout<<"name = "<<s.name<<endl;       //友元函数可以访问对应类中的所有权限下的成员
    cout<<"age = "<<s.age<<endl;        //友元函数可以访问对应类中的所有权限下的成员
}
 
 
 
 
 
 
int main()
{
    Stu s1("zhangsan", 18);
    fun(s1);
 
 
    Teacher t1("zhangpp", "C++");
    t1.display(s1);              //调用成员函数展示其他对象内容
 
 
 
 
    return 0;
}

4.5 友元的总结

1> 不到万不得已的情况下,不要使用友元,因为友元的出现使得封装称为虚谈,友元破坏了类的封装性

2> 友元不具有传递性:A是B的朋友,B不一定是A的朋友

3> 友元不具有传递性:A是B的朋友,B是C的朋友,A不一定是C的朋友

4> 友元不具有继承性:父类的朋友,不一定是子类的朋友

5> 必须使用友元的情况:插入和提取运算符重载时,只能使用友元函数来解决

五、常成员(const)

5.1 常成员的引入

在封装成员函数时,有时该成员函数,仅仅对成员变量有只读权限,不需要进行更改,那么此时就需要对成员函数进行进一步操作,达到保护成员变量的功能。

有时,在传递某个对象时,对方仅仅只是对该对象有只读权限,也需要使用相关操作,进一步保护成员变量

此时就可以引入常成员,保护成员函数和成员变量、常对象

5.2 常成员函数

1> 定义格式:定义成员函数时,在括号后加关键字 const

返回值类型 函数名(形参列表) const

2> 作用:在常成员函数中,是不允许更改成员变量的值

3> 类中常成员函数与同名的非 常成员函数构成重载关系,原因是,形参this的类型不同

非常成员函数中形参this:类名 * const this;

常成员函数中的形参this: 类名 const * const this;

4> 非 常对象,优先调用非常成员函数,如果没有该非常成员函数,那么就会调用同名的常成员函数

5.3 常对象

1> 定义格式:const 类名 对象名;

2> 常对象只能调用常成员函数,不能调用非常成员函数

5.4 mutable关键字

1> 使用该关键字修饰的成员变量,可以解除常属性,即使在常成员函数中也能更改该变量的值

#include <iostream>
using namespace std;
 
class Stu
{
private:
    mutable string name;           //使用mutable修饰的成员变量,运行在常成员函数中被修改
    int age;
 
 
public:
    Stu() {cout<<"无参构造"<<endl;}          //无参构造
    Stu(string n, int a):name(n),age(a){cout<<"有参构造"<<endl;}    //有参构造
    ~Stu(){cout<<"析构函数"<<endl;}            //析构函数
    Stu(Stu &other):name(other.name), age(other.age){cout<<"拷贝构造"<<endl;}    //拷贝构造
    Stu(Stu &&other):name(other.name), age(other.age){cout<<"移动构造"<<endl;}    //移动构造
 
 
    //定义成员函数
    void show()const        // 类名 const  * const this;
    {
        this->name = "libai";    //在常成员函数中,不能修改成员变量的值,但是可以修改由mutable修饰的成员变量
        //this->age = 100;       //普通的成员变量不能在常成员函数中被修改
 
 
        cout<<"name = "<<name<<"    age = "<<age<<endl;
    }
 
 
    void show()     // 类名  * const this;
    {
        //name = "libai";    //在常成员函数中,不能修改成员变量的值
        this->age = 100;
        cout<<"name = "<<name<<"    age = "<<age<<endl;
    }
 
 
};
 
int main()
{
    Stu s1("zhangsan", 18);            //定义一个非常对象
    s1.show();                        //非常对象,优先调用非常成员函数
 
 
    const Stu s2("lisi", 20);          //定义一个常对象
    s2.show();                        //常对象只能调用常成员函数
 
    return 0;
}

5.5 常函数

1> 定格式:const 返回值类型 函数名(形参列表){函数体内容}

2> 功能:常函数是保护函数的返回值不被修改的

#include <iostream>
using namespace std;
 
//引用函数,const修饰的函数称为常函数
const int &fun()
{
    static int num = 520;
    return num;
}
 
int main()
{
    cout<<"fun() = "<<fun()<<endl;        //对函数返回值进行读操作
 
 
    //fun() = 1314;                //对函数返回值进行写操作,但是const修饰的函数,对函数返回结果进行保护
 
 
    cout<<"fun() = "<<fun()<<endl;
 
 
    return 0;
}

5.6 关于C/C++中const的使用

1> const修饰普通变量,表示定义的是一个常变量,该变量只可读不可写,定义时必须初始化

2> const修饰指针变量时,如果放在*前表示,指针所执行空间中的内容可读不可写

如果放在*后,表示指针的值可读不可写,也就是指针指向不能改变

如果两边都加,表示都不可以更改,都是可读不可写,包含指向和值

3> const修饰函数的形参,表示传过来的数据,可读不可写

4> const修饰函数返回值时,表示保护函数的返回结果不被修改(指针函数和引用函数)

5> const修饰常成员函数,放到成员函数最后面,表示保护成员变量不被修改

  • 16
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值