C++核心笔记(四)---类和对象(C++对象模型和this指针、友元)

1、C++对象模型和this指针

1.1成员变量和成员函数分开存储

在C++中,类内的成员变量和成员函数分开存储

只有非静态成员变量才属于类的对象

#include <iostream>
using namespace std;

//成员变量 和 成员函数 分开存储的
class Person
{
    int m_A;//非静态成员变量  属于类的对象上

    static int m_B;//静态成员变量  不属于类的对象上

    void func() {}//非静态成员函数  不属于类的对象上

    static void func2() {}//静态成员函数  不属于类的对象上
};
int Person::m_B = 0;//类外初始化

void test01()
{
    Person p;
    //空对象占用内存空间为:1
    //CC++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占内存的位置
    //每个空对象也应该有一个独一无二的内存地址
    cout << "size of p =" << sizeof(p) << endl;
}

void test02()
{
    Person p;
    cout << "size of p =" << sizeof(p) << endl;
}

int main()
{
    //test01();
    test02();

    system("pause");
    return 0;
}

根据test01的输出结果得出:空对象占用内存空间为:1        (原因见程序注释)

根据test02的输出结果得出:只有非静态成员变量才属于类的对象,内存长度才会增加

1.2this指针概念

通过上节,我们知道在C++中成员变量和成员函数是分开存储的,每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码,那么问题是:这一块代码是如何区分哪个对象调用自己的呢?C++通过提供特殊的对象指针,this指针,解决上述问题。

this指针指向被调用的成员函数所属的对象

this指针的本质是指针常量

this指针是隐含每一个非静态成员函数内的一种指针

this指针不需要定义,直接使用即可

this指针的用途:
        1、当形参和成员变量同名时,可用this指针来区分
        2、在类的非静态成员函数中返回对象本身,可使用return *this

#include <iostream>
using namespace std;

//成员变量 和 成员函数 分开存储的
class Person
{
public:
    Person(int age)
    {
        //this指针指向  被调用的成员函数  所属的对象
        this->age = age;
    }

    Person& PersonAddAge(Person &p)
    {
        this->age += p.age;
        //this指向p2的指针,而*this指向的就是p2这个对象本体
        return *this;
    }

    int age;
};

//1、解决名称冲突
void test01()
{
    Person p1(18);
    cout << "p1的年龄为:" << p1.age << endl;
}
//2、返回对象本身用*this
void test02()
{
    Person p1(10);
    Person p2(10);
    //链式编程
    p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);
    cout << "p2的年龄为:" << p2.age << endl;
}

int main()
{
    //test01();
    test02();

    system("pause");
    return 0;
}

test01的输出结果为:

p1的年龄为:18
请按任意键继续. . .

test02的输出结果为:链式编程

p2的年龄为:40
请按任意键继续. . .

1.3空指针访问成员函数

C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针

如果用到this指针,需要加以判断保证代码的健壮性

#include <iostream>
using namespace std;
//#include<string>

//空指针调用成员函数
class Person
{
public:
    void showClassName()
    {
        cout << "this is person class" << endl;
    }
    void showPersonAge()
    {
        //报错原因是因为传入的指针为NULL
        //所以要加下面代码
        if (this == NULL)
        {
            return;
        }
        cout << "age=" << this->m_Age << endl;
    }
    int m_Age;
};

void test01()
{
    Person* p = NULL;
    p->showClassName();
    p->showPersonAge();//此句应该执行不了
}

int main()
{
    test01();

    system("pause");
    return 0;
}

输出结果:

this is person class
请按任意键继续. . .

1.4const修饰成员函数

常函数:

        成员函数后加const后我们成这个函数为常函数

        常函数内不可以修改成员属性

        成员属性声明时加关键字mutable后,在常函数中依然可以修改

常对象:

        声明对象前加const称该对象为常对象

        常对象只能调用常函数

#include <iostream>
using namespace std;

//常函数
class Person
{
public:
    //this指针的本质  是指针常量  指针的指向是不可以修改的
    //const Person * const this;
    //在成员函数后面加const,修饰的是this指向,让指针指向的值也不可以改变
    void showPerson() const
    {
        this->m_B = 100;
        //this->m_A = 100;
        //this = NULL;//this指针不可以修改指针的指向
    }
    void func()
    {
    }

    int m_A;
    mutable int m_B;//特殊变量,即使在常函数中,也可以修改这个值,加关键字mutable
};

void test01()
{
    Person p;
    p.showPerson();
}

//常对象
void test02()
{
    const Person p;//在对象前加const,称该对象为常对象
    //p.m_A = 100;
    p.m_B = 100;//m_B是特殊值,在常对象下也可以修改

    //常对象只能调用常函数
    p.showPerson();
    //p.func();//常对象 不可以调用普通成员函数,因为普通成员函数可以修改属性
}

int main()
{
    
    system("pause");
    return 0;
}

2、友元

生活中你的家有客厅(Public),有你的卧室(Private)。客厅所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去但是呢,你也可以允许你的好闺蜜好基友进去。

在程序里,有些私有属性 也想让类外特殊的一些函数或者类进行访问,就需要用到友元的技术
友元的目的就是让一个函数或者类 访问另一个类中私有成员

友元的关键字为 friend

友元的三种实现:
        1、全局函数做友元
        2、类做友元
        3、成员函数做友元

2.1全局函数做友元

#include <iostream>
using namespace std;
#include<string>

//全局函数做友元

//建筑物类
class Building
{
    //goodGay全局函数是Building好朋友,可以访问Building中私有成员
    friend void goodGay(Building* building);
public:
    Building()
    {
        m_SittingRoom = "客厅";
        m_BedRoom = "卧室";
    }
public:
    string m_SittingRoom;//客厅
private:
    string m_BedRoom;//卧室
};

//全局函数
void goodGay(Building *building)
{
    cout << "好基友全局函数 正在访问:" << building->m_SittingRoom << endl;
    //如果前面没声明友元,此句会报错,因为m——BedRoom是私有权限
    cout << "好基友全局函数 正在访问:" << building->m_BedRoom << endl;
}

void test01()
{
    Building building;
    goodGay(&building);
}

int main()
{
    test01();

    system("pause");
    return 0;
}

 输出结果:

好基友全局函数 正在访问:客厅
好基友全局函数 正在访问:卧室
请按任意键继续. . .

2.2类做友元

此案例有个新的做法:类内函数在类外定义

#include <iostream>
using namespace std;
#include<string>

//类做友元

//建筑物类
class Building
{
    //goodGay类是Building好朋友,可以访问Building中私有成员
    friend class GoodGay;
public:
    Building();//此构造函数在类外定义了
public:
    string m_SittingRoom;//客厅
private:
    string m_BedRoom;//卧室
};


class GoodGay
{
public:
    GoodGay();//此构造函数在类外定义了
    此函数在类外定义了
    void visit();//参观函数  访问Building中的属性
    Building* building;
};

//类外写成员函数
Building::Building()
{
    m_SittingRoom = "客厅";
    m_BedRoom = "卧室";
}
GoodGay::GoodGay()
{
    //创建建筑物对象
    building = new Building;
}

void GoodGay::visit()
{
    cout << "好基友类 正在访问:" << building->m_SittingRoom << endl;
    //如果前面没声明友元类,此句会报错
    //因为卧室是私有权限
    cout << "好基友类 正在访问:" << building->m_BedRoom << endl;
}

void test01()
{
    GoodGay gg;
    gg.visit();
}

int main()
{
    test01();

    system("pause");
    return 0;
}

输出结果:

好基友类 正在访问:客厅
好基友类 正在访问:卧室
请按任意键继续. . .

2.3成员函数做友元

#include <iostream>
using namespace std;
#include<string>

//成员函数做友元

//建筑物类声明,因为GoodGay类提到了Building类
class Building;
//GoodGay类要定义在Building类的前面,因为visit函数是GoodGay类的成员函数
//不然会报错
class GoodGay
{
public:
    GoodGay();//此构造函数在类外定义了

    //这两个函数在类外定义了
    void visit();//让visit函数可以访问Building中私有成员
    void visit2();//让visit函数不可以访问Building中私有成员

    Building* building;
};


class Building
{
    //告诉编译器  goodGay类下的visit成员函数是Building好朋友,可以访问Building中私有成员
    friend void GoodGay::visit();
public:
    Building();//此构造函数在类外定义了
public:
    string m_SittingRoom;//客厅
private:
    string m_BedRoom;//卧室
};

//类外写成员函数
Building::Building()
{
    m_SittingRoom = "客厅";
    m_BedRoom = "卧室";
}
GoodGay::GoodGay()
{
    //创建建筑物对象
    building = new Building;
}

void GoodGay::visit()
{
    cout << "visit函数 正在访问:" << building->m_SittingRoom << endl;
    //如果前面没声明友元成员函数,此句会报错
    //因为卧室是私有权限
    cout << "visit函数 正在访问:" << building->m_BedRoom << endl;
}

void GoodGay::visit2()
{
    cout << "visit函数 正在访问:" << building->m_SittingRoom << endl;
    //如果前面没声明友元成员函数,此句会报错
    //因为卧室是私有权限
    //cout << "visit函数 正在访问:" << building->m_BedRoom << endl;
}

void test01()
{
    GoodGay gg;
    gg.visit();
    gg.visit2();
}

int main()
{
    test01();

    system("pause");
    return 0;
}

输出结果:

visit函数 正在访问:客厅
visit函数 正在访问:卧室
visit函数 正在访问:客厅
请按任意键继续. . .

注意:针对成员函数做友元,注释里有一部分解释了Building类和GoodGay类谁定义前后的区别。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值