当前位置: 首页 > news >正文

C/C++语言基础--C++类数据、静态与非静态、常成员、友员、成员变量与函数指针等相关知识点

本专栏目的

  • 更新C/C++的基础语法,包括C++的一些新特性

前言

  • 通过前面几节,我们介绍了C++的类与对象、构造与析构函数、拷贝等相关知识,这一篇将详细介绍了C++的成员变量相关的知识点与扩展
  • C语言后面也会继续更新知识点,如内联汇编;
  • 本人现在正在写一个C语言的图书管理系统,1000多行代码,包含之前所学的所有知识点,包括链表和顺序表等数据结构,请大家耐心等待!!预计国庆前写完更新。

文章目录

    • 类中数据与函数储存
    • 静态(static)成员
      • 静态成员变量
      • 静态成员函数
      • 普通成员函数的内部处理
      • pthis对象指针与this指针
    • 常(const)成员
      • const成员变量
      • const成员函数
        • mutable
      • 注意
    • 友元(了解即可)
      • 友元函数
      • 友元类
      • 注意
    • 成员指针
      • 成员变量指针
        • 非静态成员变量指针
        • 静态成员变量指针
      • 成员函数指针(类)
        • 非静态成员函数指针

类中数据与函数储存

C++的数据主要是对类的抽象,类是面向对象的抽象,在C++面向对象程序设计中,主要有两部分:

  • C++语言中构造、析构函数、虚函数、继承、多态等是是支持面向对象程序设计直接体现
  • C++中这个底层机制为上面这一点做支撑

故从以上点来看类数据储存

  • C++中的类从面向对象理论出发,将变量(属性)和函数(方法)集中定义在一起,用于描述现实世界中的类。
  • 从计算机的角度,程序依然由数据段和代码段构成。

接下来思考下面程序运行的结果?,你感觉他会输出的值是多少呢?

class C1
{
public:int i;int j;int k;static int number;   // 静态
};class C2
{
public:int i;int j;int k;int getK(){return k;}    // 成员函数void setK(int nk){i = nk;}   // 成员函数
};void test()
{cout<<"c1:%d "<<sizeof(C1)<<endl;cout<<"c2:%d "<<sizeof(C2)<<endl;
}

输出结果:

c1:%d 12
c2:%d 12

原因

  • 成员变量
    • 普通成员变量:存储在对象中(对象存储位置由创建位置决定),与struct对象有相同的内存布局和字节对齐方式(尤其要注意对其方式)
    • 静态成员变量:存储在静态变量区(全局区),这个下一节也将会详细讲解
  • 成员函数
    • 存储在代码段中,而对象储存位置由创建的位置决定。

问题出来了:很多对象共用一块代码?代码是如何区分具体对象的那?

换句话说:int getK() { return k; },代码是如何区分,具体obj1、obj2、obj3对象的k值?

静态(static)成员

C++是对类的抽象,故在C++中类中静态成员分为静态成员变量静态成员函数,创建静态成员变量静态成员函数只需要用关键之static

静态成员在内存中存储在全局区,声明周期是从程序开始到结束,故其他成员可以贡献其资源。

静态成员变量

  • static 成员变量和普通 static 变量一样,都在内存分区中的全局数据区分配内存,到程序结束时才释放,也就是他不占用类的内存空间(类的生命周期是创建到类的销毁,而销毁可能是超出作用域,也可能手动释放)

  • 静态成员变量必须在类的内部申明,在类的外部定义,除非是使用内联,因为静态成员变量不属于对象,他在全局区,在类或者结构体中只是声明 ,后面必须要定义

class W
{static int s_cnt;			//类的内部声明inline static int s_num;	//C++17 可以直接内联,就不需要在类的外部定义了
};
int W::s_cnt = 0;		//类的外部定义
  • 访问权限:既可以使用对象访问,也可以使用类名访问

在这里插入图片描述

静态成员函数

  • 在类中,static 除了可以声明静态成员变量,还可以声明静态成员函数。
  • 使用权限:普通成员函数可以访问所有成员(静态和非静态都可以),静态成员函数只能访问静态成员

比如说,我们要通过函数获取学生总人数(m_classSize),有两种方法:

  • 定义普通成员函数:可以访问静态成员,但是这个函数只对静态成员进行操作,加上 static 语义更加明确。
  • 定义静态成员函数:在函数前面加上static,可以声明为静态函数

静态成员函数调用方式,有两种:

  • 和普通成员函数一样,通过对象去调用:

  • Student stu;
    stu.classSize();
    
  • 不定义对象,直接通过类名::函数名去调用

  • Student::classSize();
    

思考:为什么静态成员函数不能调用类的非静态成员?

在类的定义中,变量内存分配和函数是不同的,类的内存大小=类的成员变量内存大小,而成员函数是在创建对象的时候要调用的时候创建的,这样有利于节约内存,而静态成员函数存储在全局区,一定义就创建出来了,如果在静态成员函数中调用类的成员函数的话,就会发现那个成员函数根本不存在,总的来说就是:

  • 上下文环境不同
  • this指向不同

普通成员函数的内部处理

左边为C++代码,右边为C语言代码

在这里插入图片描述

其实把C++的类改为C语言,只需要用结构体+函数,然后通过传对象的指针,在函数里面访问即可!(右边的代码)

pthis对象指针与this指针

通过C语言的方式,对成员变量进行修改、赋值…………操作,用C风格方式,可能的采用指针的方法,但是这样也有一个问题,如下:

在这里插入图片描述
上面问题就是,pthis这个指针指向修改了,并不是指向原有的位置。

那么有没有什么办法可以让pthis指针在函数里面不能被修改呢?

  • 可以给pthis指针加上const属性,让它不能被改变指向
    在这里插入图片描述

而这个加上const的pthis指针,就是C++的this指针。

注意:

  • this 是 C++ 中的一个关键字,也是一个 const 指针,它指向当前对象,在类的内部可以通过它可以访问当前对象的**所有成员*,如下是他的使用操作
class Stu
{
public:Stu(int age, char* name){this->age = age;}
private:int age;
}
  • 以上发现,如果函数中形参和类的属性名相同,可以通过this解决。

常(const)成员

在类中,如果你不希望某些数据被修改,可以使用const关键字加以限定。const 可以用来修饰成员变量和成员函数的关键字

const成员变量

const 成员变量的用法和普通 const 变量的用法相似,只需要在声明时加上 const 关键字。

  • 初始化 const 成员变量只有一种方法,就是通过构造函数的初始化列表
class Test
{
public:Test() {};  // 报错:error C2789:“Test::a”:必须初始化常量限定类型的对象const int m_a;
}
  • 必须通过构造函数的初始化列表初始化,初始化之后不允许赋值。(但是初始化列表可以)
class Test
{
public:Test(int a): m_a(8){m_a = 2;  // error C2166:左俏指定 const 对象}const int m_a;
}// 注意:这种方法不行
class Test
{
public:Test(int a){m_a = a;   // 会报错}
public:const int m_a;
};

const成员函数

const 成员函数可以使用类中的所有成员变量,但是不能在函数里面修改它们的值,通过const 成员函数也称为常成员函数。

但要注意,类中的常成员函数中的const放的位置有点独特,放在函数后面,如下:

class Text
{
public:void fix() const   // 放到这里{m_age = 2;   // 会报错}
private:int m_age;
}
mutable

在C++中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中,如上面程序可以做修改:

class Text
{
public:void fix() const   // 放到这里{m_age = 2;   // 这样就不会报错了}
private:mutable int m_age;
}

注意

  • 常成员函数可以访问任何成员
  • 常对象只能访问常函数,这个很容易忽略,但是编译器编译的时候,我们也很容易发现错误更改。

友元(了解即可)

在C++中,私有成员只能在类的成员函数内部访问,如果想在别处访问对象的私有成员,只能通过类提供的接口间接地进行,所以C++引入友元的概念,使得被定义为友元的函数即使在外部,也能访问类中的私有成员。

友元函数

  • 全局函数作为友元函数
class GirlFriend
{//在public之前friend void stage(GirlFriend& girl);
private:int data;
public:GirlFriend(int data):data(data){}
};void stage(GirlFriend& girl)
{GirlFriend t(33);cout << "data: " << girl.data<<" " << t.data;
}int main()
{GirlFriend g(2);stage(g);return 0;
}

我们在GirlFriend类中把stage函数声明为了友元函数,这样子就可以在stage函数中访问GirlFriend类的私有成员了~

  • 成员函数作为友元函数
class GirlFriend;       //必须提前声明
class BoyFriend
{
public:BoyFriend() = default;void kissGirlFriend(GirlFriend* girlFriend);
};class GirlFriend
{friend void stage(GirlFriend& girl);friend void BoyFriend::kissGirlFriend(GirlFriend* girlFriend);
private:int data;
public:GirlFriend(int data):data(data){}
};// 实现顺序一一定要注意
void BoyFriend::kissGirlFriend(GirlFriend* girlFriend)   //必须在类外实现
{cout << "kiss kiss " << girlFriend->data << endl;
}int main()
{GirlFriend g(2);BoyFriend boy;boy.kissGirlFriend(&g);return 0;
}

一个类的成员函数作为另一个类的友元函数的时候,这个成员函数必须在类外实现,而且是必须在作为友元的类之后实现。

  • 类做友元

  • 注意点

    • 不能把别的类的私有函数定义为友元。
    • 一个函数可以被多个类声明为友元函数,这样就可以访问多个类中的 private 成员
    • 尽量不要用,故了解即可,忘了在查

友元类

//在哪一个类中声明为友元,则本类的**成员函数**就可以访问那一个类的私有成员
class BoyFriendclass GirlFriend
{friend class BoyFriend;
private:int data;
public:GirlFriend(int data):data(data){}
};class BoyFriend
{
private:
public:void bedExercise(GirlFriend* girl){cout << __FUNCTION__ << " " << girl->data;}
};int main()
{GirlFriend g(234);BoyFriend().bedExercise(&g);return 0;
}

GirlFriend类中,把BoyFriend类声明为了友元类,此时在BoyFriend类中,就可以随心所欲的访问 GirlFriend类的私有成员了!

注意

友元函数有优点也有缺点,用好了是神器,用坏了是毁灭!

**优点:**能够提高效率,表达简单、清晰

缺点: 友元函数破环了类的封装性,尽量使用成员函数,除非不得已的情况下才使用友元函数

建议:常中用的也不多,用到忘了在查也行

成员指针

下面内容,了解即可,需要实际情况学习,忘了现查也行。

成员变量指针

非静态成员变量指针

定义成员变量指针:

 int A::*p = &A::num;   // 注意:*p是定义的,在A类中没用被定义

通过成员变量指针访问成员变量:

A a(22);
cout<<" "<<a.*p;   // ** 必须 **通过对象去访问
静态成员变量指针

定义静态成员变量指针:

int *p = &A::static_number;

可以通过对象访问

A a(22);
cout<<" "<<a.*p;
// 也可以,直接输出
cout << *p << endl;  

成员函数指针(类)

非静态成员函数指针

定义成员函数指针:

void (A:: * pshow)() = &A::show;           //对于成员函数来说必须取地址
//A:: show()     这是类的真正原型,在类中只是省略了

通过成员函数指针调用成员函数

//在类外调用 , 必须通过对象调用
(a.*pshow)();              //必须先实例化对象
//在类内调用
(this->*pshow)();

静态成员函数指针

定义静态成员函数指针:(存在全局区里)

void (* pstatic_show)() = &A::static_show;

不可以通过对象访问

cout<<" "<<(*pstatic_show());

也可以直接访问

cout<<" "<<pstatic_show();
  • 定义静态成员指针时候,左边都不加 对象:: ,只有定义非静态成员才会加 (A:: *show)() 。但是右边一样,都要加&

http://www.mrgr.cn/news/36130.html

相关文章:

  • 3. 轴指令(omron 机器自动化控制器)——>MC_MoveZeroPosition
  • uboot — uboot命令的使用
  • 如何在 Linux 中管理和清理日志文件( `find` 命令按时间批量删除日志)
  • 2024.9.25 作业和思维导图
  • 线程安全的数据结构使用起来一定线程安全吗?
  • 将ipad作为数位板使用教程/出现延迟拖拽怎么办?
  • MySql 从入门到入门
  • 【笔记篇】一篇文章搞定Spring框架
  • WordPress LearnPress插件 SQL注入复现(CVE-2024-8522)
  • 每天分享一个FPGA开源代码(6)- 浮点数运算
  • 回归阅读第一本:《瓦尔纳宝典》
  • windows GetUserNameEx api使用c++
  • RTE 大会报名丨AI 时代新基建:云边端架构和 AI Infra ,RTE2024 技术专场第二弹!
  • visio 2021入门直通车(一天全搞定)
  • 二维环境下TDOA的MATLAB仿真代码(4个锚节点)
  • project modules模块消失,只显示module,不显示project
  • 深入理解包管理工具
  • 【Golang】Go语言中如何面向对象?
  • 二分查找及变体
  • shell配置文件介绍