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

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

时间:2024-09-25 20:20:16浏览次数:10  
标签:知识点 const 函数 静态 成员 C++ int 变量

本专栏目的

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

前言

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

文章目录

类中数据与函数储存

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 BoyFriend;
class 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)() 。但是右边一样,都要加&

标签:知识点,const,函数,静态,成员,C++,int,变量
From: https://blog.csdn.net/weixin_74085818/article/details/142532005

相关文章

  • Day1 c++(发展历史,特点,面向对象核心术语,开发环境,新建项目,引用,窄化,输入,字符串,函数)
    1.C++发展历史(了解)1983年,贝尔实验室(BellLabs)的BjarneStroustrup发明了C++。C++在C语言的基础上进行了扩充和完善,是一种面向对象程序设计(OOP)语言。Stroustrup说:“这个名字象征着源自于C语言变化的自然演进”。还处于发展完善阶段时被称为“newC”,之后被称为“Cwi......
  • 【C++】队列
    示意图什么是队列队列(queue)是一种具有先进入队列的元素一定先出队列性质的表。由于该性质,队列通常也被称为先进先出(firstinfirstout)表,简称FIFO表。就像排队一样,最先到的人也就最先买到单,优先离开队伍头文件与声明头文件#include<queue>声明定义queue<G>qu......
  • 【C++】面向对象编程的三大特性:深入解析继承机制
    C++语法相关知识点可以通过点击以下链接进行学习一起加油!命名空间缺省参数与函数重载C++相关特性类和对象-上篇类和对象-中篇类和对象-下篇日期类C/C++内存管理模板初阶String使用String模拟实现Vector使用及其模拟实现List使用及其模拟实现容器适配器Stack与QueuePriority......
  • c语言中字符串输入的相关知识点
    (1)scanf只能接收非空格字符串遇到空格或者换行就算结束。代码如下:#include<stdio.h>#include<stdlib.h>intmain(){chararr[10];scanf("%s",&arr);printf("%s",arr);}(2)gets函数-能够接收空格,但是不能接收回车#include<stdio.h>#include&l......
  • centos7 设置静态ip
    在CentOS7上设置静态IP,你需要按照以下步骤操作: 找到你的网络接口名称,使用命令 nmclid。 编辑网络配置文件。配置文件通常位于 /etc/sysconfig/network-scripts/,文件名为 ifcfg-<interface>,例如 ifcfg-ens33。 修改配置文件,设置静态IP相关的参数。 重......
  • unity调用java静态方法
    在Unity中调用Java静态方法通常需要通过Android插件实现。以下是基本步骤:创建Java类:在AndroidStudio中创建一个Java类,包含静态方法。packagecom.example.myplugin;publicclassMyJavaClass{publicstaticStringmyStaticMethod(){return"Hello......
  • c++ 静态私有成员
    1.概要1.问题提出价值:关于静态和私有的问题,静态的且是私有的,有什么价值呢,就是只有在类的函数中可以使用和赋值,这有一个隔离的价值。矛盾:静态成员必须在类的外部初始化,初始化那就要赋值,赋值就要访问,这和上面视乎矛盾?2.调查结果结论:静态私有成员可以在类的外部初始化;3.相关......
  • C++开发项目
    1.项目系统需求文章目录1.项目系统需求功能如下:2.创建项目:3.创建管理类3.1创建文件3.2头文件实现3.3源文件实现4.菜单功能4.1添加成员函数4.2菜单功能实现4.3测试菜单功能5.退出功能5.1提供功能接口5.2实现退出功能5.3测试功能运行效果图:6.创建职工类6.1创建职工抽象类6.2创建普......
  • 【每周例题】蓝桥杯 C++ 数树数
    数树数题目数树数题目分析通过图片的二叉树,我们可以发现每一个·分支的L=2a-1R=2a代码#include<iostream>#include<string>usingnamespacestd;chars[50];inta;intmain(){intn,q;cin>>n>>q;for(inti=0;i<q;i++){......
  • 计算机组成原理408第五章中央处理器知识点快速回顾二轮复习
    CPU由运算器和控制器这样的两大部件来组成,那么运算器由ACC累加寄存器,乘商寄存器,还有若干个通用寄存器以及算术逻辑单元alu来组成。那除了这些寄存器,还有alu之外,还会有一些其他的重要的寄存器,我们在之前没有介绍过。运算器主要负责对数据进行处理,也就是算术运算和逻辑运......