首页 > 编程语言 >C++学习笔记(四)类和对象

C++学习笔记(四)类和对象

时间:2024-09-03 10:53:33浏览次数:13  
标签:p2 const 对象 age 笔记 Person C++ void 指针

类和对象

C++对象模型和this指针

成员变量和成员函数的存储

C++中的成员变量和成员函数是分开存储的,只有非静态成员变量才属于类的对象上

class Person{

    int m_Age;//非静态成员变量
    static  int m_B;//静态成员变量
    void func(){ //不属于类的对象上

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

Person 的对象p只有 m_Age的大小也就是4个字节。

this指针:

在之前的笔记中写过, 每个非静态成员变量只诞生一份实例,也就是说多个同类对象共用一块代码,那么问题来了,这一块代码是如何区分到底是哪个对象调用了自己的?

那么C++通过提供特殊的对象指针,this指针,解决上述问题。this指针指向被调用成员函数所属的对象

this指针特性:

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

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

用途:

1.当行参和成员变量同名时,可以用this来区分。

class Person{
    public:
    Person(int age){
        age=age;
    }
    int  age;
};
void test01(){
    Person p;
    cout<<p.age<<endl;

}
int main(){
    test01();
    return 0;
}

上述例子中运行后会提示错误,这个错误就是因为age并不清楚哪个是成员变量哪个是参数传入的。

那就可以用this来区分一下。

//把     
   age=age;
//改为
this->age=age;


在类的非静态成员函数中返回对象本身,可使用return *this

class Person{
public:
    Person(int age){
        this->age=age;
    }

    void addAge(Person p){
        this->age+=p.age;
    }
    int  age;
};
//2.返回对象本身用*this
void test02(){
    Person p1(10);
    Person p2(15);
    p2.addAge(p1);
    cout<<p2.age<<endl;

}

此时的运行结果是10+15=25,那如果我想要多加几次,可不可以直接:

p2.addAge(p1).addAge(p1);

这样肯定是不行,从语法来说就不对因为我们返回值是void,但是我们最终目的是多加几次,应该如何做呢?

如果我们让这个函数执行结果return的是p2本身,执行一次后就保存在p2了,是否就可以多次执行了

class Person{
public:
    Person(int age){
        this->age=age;
    }

    Person& addAge(Person &p){
        this->age+=p.age;
        return *this;
    }
    int  age;
};
void test02(){
    Person p1(10);
    Person p2(15);
    p2.addAge(p1).addAge(p1);
    cout<<p2.age<<endl;

}

这个时候代码就不报错了,结果就变成了35了。当然依次类推,由于返回的是本身。所以如果我们这样加结果是多少呢?

    p2.addAge(p1).addAge(p1).addAge(p2);

后面再跟一个参数是p2的addAge,结果应该是70,因为前面p2年龄已经是35了,又传入了35加起来就是70了。

如果上面的返回值不是引用了,而是值,那么结果就会变为25,因为每次 addAge 都返回一个临时对象,并且每次调用都在新的临时对象上进行,因此这些操作都不会影响 p2 本身。

p2 的 age 在第一次调用后变为 25,之后的操作不再对 p2 本身产生影响。

这被称为是链式编程思维

空指针访问成员函数

class Person{
public:

    void showClassName(){
        cout<<"1111 Person Class"<<endl;
    }
    void  showPersonAge(){
        cout<<m_Age<<endl;
    }

    int m_Age;
};
void test01(){
    Person *p =NULL;
    p->showClassName();
    p->showPersonAge();
}

这里我们运行后程序会中断,原因在于showPersonAge() 这个函数身上。因为在

        cout<<m_Age<<endl;

其实默认是有一个this->的

        cout<<this->m_Age<<endl;

但是因为p是个空指针,所以会找不到这个数据,这当然会出错。

为了程序不断掉,我们可以这样做

    void  showPersonAge(){
        if(this==NULL)
            return;
        cout<<m_Age<<endl;
    }

加一个空指针的判断。

const修饰成员函数

常函数:

  • 加上const修饰的函数称之为常函数
  • 常函数不能修改成员属性(只读)
  • 成员属性声明时加上关键字mutable后,在常函数中依然可以修改
class  Person{
    
public:
    void showPerson()const
    {
        m_A=100;
    }
    int m_A;
};

const修饰后,我们就无法对m_A进行修改了。

我们怎么理解呢?

其实原函数也可以写为

    void showPerson()const
    {
        this->m_A=100;
    }

每个成员函数都有一个this指针,this指针的本质就是指针常量,指针的指向是不可修改的。如果你在函数内部对this指针赋值,会报错,因为它是const的其实对于本例this等价于Person * const this。那如果想要this指向的值都不可修改就要在Person前面再加上const,就变成了:

const Person * const this

这个const其实就是void showPerson() const。其本质修饰的就是this指针指向的值。

同样,我们想要在常函数中也可以修改这个值的话。就加上mutable关键字

class  Person{

public:
    void showPerson()const
    {
        m_A=100;
    }
    mutable int m_A;
};


常对象:

  • 加上const修饰的对象称为常对象
  • 常对象只能调用常函数

 

class  Person{

public:
    void showPerson()const
    {
        m_A=100;
//        m_B=1000;
    }
    mutable int m_A;
    int m_B;
};

//常对象
void test01(){

    const Person p;
    p.m_B=100;//报错
}

由于const修饰了p,m_B并不是const修饰的,所以这里也会报错。

但是

p.m_A=100;//就可以了因为有mutable修饰。

接下来我们在Person类中加上一个空的函数

    void func(){
        
    }

使用静态修饰的对象p来调用,同样也会报错,因为常对象只能调用常函数,由于普通成员函数可以修改属性值,但是常函数不能修改值,如果允许我们这样做就会发生冲突了。

标签:p2,const,对象,age,笔记,Person,C++,void,指针
From: https://blog.51cto.com/u_16160587/11906993

相关文章

  • c++入门基础
    欢迎来到c++入门基础的学习目录1、第一个c++程序2、命名空间3、c++的输入与输出4、缺省参数5、函数重载6、详解引用(引用属于重点知识)7、指针与引用的差别8、内联函数9、nullptr在学习c++之前我们要知道c++是什么—c++是c语言的扩展,主要在c语言之上添加了封装、继......
  • 10 Python面向对象编程:类和对象以及和Java的对比
    本篇是Python系列教程第10篇,更多内容敬请访问我的Python合集这里只介绍类和对象,self、属性、方法、访问控制、类继承、方法重写在后面的文章里介绍在Python中,类和对象是面向对象编程的基础。1类的概念类是一种创建对象的蓝图或模板。它定义了一组属性(变量)和方法(函......
  • 11 Python面向对象编程:三大特性,封装、继承、多态
    本篇是Python系列教程第11篇,更多内容敬请访问我的Python合集1封装封装就是把类的公有属性改成私有属性,并且提供对外访问的方法。示例classMyClass:def__init__(self,value):self.__value=valuedefget_value(self):returnself.__......
  • 笔记:《利用Python进行数据分析》之透视表和交叉表
    透视表和交叉表透视表(pivottable)是各种电子表格程序和其他数据分析软件中一种常见的数据汇总工具。它根据一个或多个键对数据进行聚合,并根据行和列上的分组键将数据分配到各个矩形区域中。在Python和pandas中,可以通过本章所介绍的groupby功能以及(能够利用层次化索引的)重塑运......
  • 算法题笔记
    时间和空间复杂度:referdoc小记:时间复杂度是用大写的“O”来表示的,比如:O(1),O(n),O(logn),O(nlogn),O(n²)等常量可以被忽略。例如5n,换成大O表示法就是O(n)。对于算法的空间复杂度也可以简单的进行总结一下:如果申请的是有限个数(常量)的变量,空间复杂度为O(1)。如果申请的......
  • 读书笔记:高效C/C++调试
    高效C/C++调试(美)严琦、卢宪廷目录第1章调试符号和调试器11.1调试符号11.1.1调试符号概览2全局变量文件行号数据类型1.1.2DWARF格式31.2实战故事1:数据类型的不一致141.3调试器的内部结构161.3.1用户界面161.3.2符号管理模块161.3.3目标管理模块......
  • C++(static_cast)
    目录1.语法2.示例3.为什么选择static_cast总结static_cast是C++中的一种类型转换运算符,用于在不同的数据类型之间进行安全转换。与C风格的强制类型转换不同,static_cast更加安全和明确。它主要用于进行类型转换时,确保转换是合法的,并且不会引入不必要的风险。1.语法......
  • A-计算机毕业设计定制:80891ssm大学校园慈善拍卖网站(免费领源码)可做计算机毕业设计JAV
    摘要信息化社会内需要与之针对性的信息获取途径,但是途径的扩展基本上为人们所努力的方向,由于站在的角度存在偏差,人们经常能够获得不同类型信息,这也是技术最为难以攻克的课题。针对大学校园慈善拍卖网站等问题,对大学校园慈善拍卖网站进行研究分析,然后开发设计出大学校园慈善......
  • 计算机三级 - 数据库技术 - 第十章 数据库运行维护与优化 笔记
    第十章数据库运行维护与优化 内容提要:了解数据库运行维护的基本原理了解运行状态监控与分析了解数据库存储空间管理掌握数据库性能优化的方法10.1数据库运行维护基本工作DBAS进入运行维护阶段的主要任务:保证数据库系统安全、可靠且高效地运行维护工作包括:数......
  • 计算机三级 - 数据库技术 - 第九章 安全管理 笔记
    第九章安全管理内容提要:理解安全控制的基本概念了解SOLServer2008的存取控制掌握SQLServer2008的安全控制实现方式了解Oracle的安全管理9.1安全控制概述9.2存取控制自主存取控制又称自主安全模式,通过SQL的GRANT,REVOKE,DENY语句来实现。权限......