类和对象 --- 封装+对象特性

news2024/11/29 4:49:56

👂 快乐E调 - 林澜叶 - 单曲 - 网易云音乐

👂 Plain Jane (Freestyle) - Ombre2Choc Nation - 单曲 - 网易云音乐

1.5倍速,跟着敲,初识C++

目录

🏆封装

🌳属性和行为作为整体

🌳案例 -- 设置学生类

🌳访问权限

🌳class和struct区别

🌳成员属性私有化

🌳案例1 -- 立方体类

🌳案例2 -- 点和圆关系

🏆对象特性

🌳构造函数和析构函数

🌳函数的分类以及调用

🌳拷贝构造函数调用时机

🌳构造函数调用规则

🌳深拷贝与浅拷贝

🌳初始化列表

🌳类对象作为类成员

🌳静态成员

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

🌳this指针的用途

🌳空指针访问成员函数

🌳const修饰成员函数


🏆封装

封装,将数据和操作数据的函数绑定在一起,形成一个类(class)

数据被隐藏在类的内部,只有类的内部函数才能访问这些数据

外部代码只能通过类提供的接口访问数据,而不能直接访问数据,从而保证了数据的安全性和稳定性

封装包括公有属性、私有属性和保护属性

公有属性是可以被外部代码访问的属性,私有属性是只能在类内部访问的属性,而保护属性介于公有属性和私有属性之间,可以被派生类访问

🌳属性和行为作为整体

封装的意义

属性和行为作为一个整体来表现事物

#include<iostream>
using namespace std;

const double PI = 3.14;

//设计一个圆类,求圆的周长
//圆周长公式:2 * PI * 半径

//class代表设计一个类,类后面紧跟着类名称
class Circle
{
    //访问权限
    //公共权限
public:


    //属性
    int m_r;

    //行为
    //获取圆周长
    double calculateZC()
    {
        return 2 * PI * m_r;
    }
};

int main()
{
    //通过圆类 创建具体的圆(对象)
    Circle cl; //通过类创建具体对象
    //给圆对象 的属性进行赋值
    cl.m_r = 10; //对象cl的m_r属性, .翻译为的

    //2 * PI * 10 = 62.8
    cout<<"圆的周长为:"<<cl.calculateZC()<<endl;

    system("pause");
    return 0;
}
圆的周长为:62.8
请按任意键继续. . .

🌳案例 -- 设置学生类

#include<iostream>
using namespace std;

//学生类, 姓名和学号作为属性
class Student
{
public:
    //类中的属性和行为,统称成员
    //属性:成员属性  成员变量
    //行为:成员函数  成员方法
    string m_name;
    long long m_id;
    //属性

    //行为
    void showStudent()
    {
        cout<<"姓名:"<<m_name<<"\t学号:"<<m_id<<endl;
    }

    //给姓名赋值
    void setName(string name)
    {
        m_name = name;
    }

    //给学号赋值
    void setId(long long id)
    {
        m_id = id;
    }

};

int main()
{
    Student s1;
    s1.setName("张三三");
    s1.m_id = 20152005035;
    s1.showStudent();

    Student s2;
    s2.m_name = "渣渣辉";
    s2.setId(200221);
    s2.showStudent();

    return 0;
}
姓名:张三三    学号:20152005035
姓名:渣渣辉    学号:200221
 

🌳访问权限

#include<iostream>
using namespace std;

//访问权限
//公共public 保护protected 私有private
//公共权限  成员  类外  可访问
//保护权限  类外不可访问, 儿子可以访问父亲中保护的内容
//私有      类外不可访问, 儿子不可访问父亲的私有内容

class Person
{
public:
    //公共权限
    string m_Name; //姓名
protected:
    //保护权限
    string m_Car; //汽车
private:
    //私有权限
    int m_Password; //银行卡密码
public:
    void func()
    {
        m_Name = "渣渣辉";
        m_Car = "拖拉机";
        m_Password = 213134;
    }
};

int main()
{
    //实例化具体对象
    Person p1;

    p1.m_Name = "刷卡";
    //p1.m_Car = "奔驰"; //保护权限内容,类外无法访问
    //p1.m_Password = 213; //私有权限内容,类外无法访问

    return 0;
}

🌳class和struct区别

#include<iostream>
using namespace std;

class C1
{
    int m_A; //默认权限 私有, 不写public就是私有
};

struct C2
{
    int m_A; //默认权限 公共
};

int main()
{
    //struct 和 class区别
    //struct 默认权限是 公共 public
    //class 默认权限是  私有 private
    C1 c1; //通过类C1实例化对象c1
    //c1.m_A = 100; //报错, 私有成员
    C2 c2;
    c2.m_A = 100; //而struct默认权限  公共, 可以访问


    return 0;
}

🌳成员属性私有化

1,可以自己控制读写权限(public中读,写的函数)

2,防止数据超出有效范围(if判断)

#include<iostream>
using namespace std;

//成员属性私有化
//1,可以自己控制读写权限
//2,对于写可以检测数据有效性

class Person
{
public:
    //设置姓名
    void setName(string name)
    {
        m_Name = name; //读
    }
    //获取姓名
    string getName()
    {
        return m_Name; //写
    }
    //获取年龄
    int getAge()
    {
        //m_Age = 0; //初始化0岁
        return m_Age;
    }
    //设置情人 只写
    void setLover(string love)
    {
        m_Lover = love;
    }
    //设置年龄
    void setAge(int age)
    {
        if(age < 0 || age > 150) {
            m_Age = 0;
            cout<<"输错了"<<endl;
            return;
        }
        m_Age = age;
    }
private:
    string m_Name; //姓名  可读可写
    int m_Age; //年龄  可读可写(0 ~ 150岁之间)
    string m_Lover; //情人  只写
//对外提供public接口
};

int main()
{
    Person p;
    p.setName("张三");
    cout<<"姓名为:"<<p.getName()<<endl; //调用需要先p.

    p.setAge(122);
    cout<<"年龄为:"<<p.getAge()<<endl;
    p.setLover("苍劲空");


    return 0;
}
姓名为:张三
年龄为:122
 

🌳案例1 -- 立方体类

1,成员函数

成员函数是指属于某个类的函数,它们可以访问类的私有成员和保护成员 

2,全局函数

全局函数是指不属于任何类的函数,它们不能直接访问类的私有成员和保护成员

3,区别

对于一个类的成员函数来说,它们可以访问类的私有成员和保护成员,因此可以更加方便地对类的数据进行操作。而全局函数则不能直接访问类的私有成员和保护成员,需要通过类的接口函数来间接访问

#include<iostream>
using namespace std;

//立方体类设计
//1,创建立方体类
//2,属性和行为
//3,立方体面积和体积
//4,分别利用全局函数和成员函数 判断两个立方体是否相等

class Cube
{
public:
    //设置长
    void setL(int l)
    {
        m_L = l;
    }
    //获取长
    int getL()
    {
        return m_L;
    }
    //宽
    void setW(int w)
    {
        m_W = w;
    }
    int getW()
    {
        return m_W;
    }
    //高
    void setH(int h)
    {
        m_H = h;
    }
    int getH()
    {
        return m_H;
    }
    //面积
    int calculateS()
    {
        return 2 * (m_L*m_W + m_L*m_H + m_W*m_H);
    }
    //体积
    int calculateV()
    {
        return m_L * m_W * m_H;
    }
    //成员函数在类里面
    //利用成员函数判断两立方体是否相等
    bool isSameByClass(Cube &c)
    {
        //自身和传进来的相比较
        if(m_L == c.getL() && m_W == c.getW() && m_H == c.getH())
            return true;
        return false;
    }
private:
    int m_L, m_W, m_H; //长宽高length, width, height
};

//全局函数在类外面
//利用全局函数判断  两个立方体是否相等
bool isSame(Cube &c1, Cube &c2)
{
    if(c1.getL() == c2.getL() && c1.getW() == c2.getW() && c1.getH() == c2.getH())
        return true;
    return false;
}

int main()
{
    //创建立方体对象
    Cube c1;
    c1.setL(10);
    c1.setW(8);
    c1.setH(2);

    cout<<"c1的面积:"<<c1.calculateS()<<endl;
    cout<<"c1的体积:"<<c1.calculateV()<<endl;

    //创建第2个立方体
    Cube c2;
    c2.setL(10);
    c2.setW(8);
    c2.setH(5);

    //利用全局函数判断
    bool ret = isSame(c1, c2);
    if(ret) cout<<"c1 和 c2相等"<<endl;
    else cout<<"c1 和 c2不相等"<<endl;

    //利用成员函数判断
    ret = c1.isSameByClass(c2);
    if(ret) cout<<"成员函数判断:c1 和 c2相等"<<endl;
    else cout<<"成员函数判断:c1 和 c2不相等"<<endl;

    return 0;
}
c1的面积:232
c1的体积:160
c1 和 c2不相等
成员函数判断:c1 和 c2不相等
 

🌳案例2 -- 点和圆关系

#include "point.h"中,双引号表示该头文件是自定义的

原来分文件的编写要创建项目的。。。搞了半小时才明白

大概是这样

以下是项目中的分文件代码

main.cpp

//main函数 只留下全局函数 和 main函数的测试代码

//ctrl + shift + c批量注释
#include<iostream>
using namespace std;
#include "circle.h" //圆类头文件
#include "point.h" //点类头文件


//全局函数:判断点和圆关系
void isInCircle(Circle &c, Point &p)
{
    //计算两点距离平方
    //c.getCenter()表示Point类
    int dist =
    (c.getCenter().getX() - p.getX()) * (c.getCenter().getX() - p.getX()) +
    (c.getCenter().getY() - p.getY()) * (c.getCenter().getY() - p.getY());

    //计算半径平方
    int rDistance = c.getR() * c.getR();
    //判断关系
    if(dist == rDistance)
        cout<<"点在圆上"<<endl;
    else if(dist > rDistance)
        cout<<"点在圆外"<<endl;
    else
        cout<<"点在圆内"<<endl;
}

int main()
{
    //创建圆
    Circle c;
    c.setR(10);
    Point center;
    center.setX(10);
    center.setY(0);
    c.setCenter(center);

    //创建点
    Point p;
    p.setX(10);
    p.setY(9);
    //判断关系
    isInCircle(c, p);

    return 0;
}

point.h

//point.h 点类  函数的声明

#pragma once //防止头文件重复包含
#include<iostream>
using namespace std; //标准命名空间

//粘贴过来之后, 只需要成员函数的声明, 删掉实现

//点类
class Point
{
public:
    //设置x, 获取x
    void setX(int x);
    int getX();

    //设置, 获取y
    void setY(int y);
    int getY();

private:
    int m_X, m_Y;

};

point.cpp

//point.cpp 点类的实现

#include "point.h" //包含之前.h头文件
//粘贴过来后,只需要留下函数实现
//同时, 每个函数前加上作用域


//点类
//class Point
//{
//public:
//设置x, 获取x
void Point::setX(int x) //加上作用域,表示Point作用域下成员函数
{
    m_X = x;
}
int Point::getX()
{
    return m_X;
}
//设置, 获取y
void Point::setY(int y)
{
    m_Y = y;
}
int Point::getY()
{
    return m_Y;
}

//private:
    //int m_X, m_Y;

//};

circle.h

//circle.h 圆类  函数的声明

#pragma once
#include<iostream>
using namespace std;
//一个类中用到另一个类,只需要把另一个类的头文件包含进来
#include "point.h"

//圆类
class Circle
{
public:
    //设置获取半径
    void setR(int r);
    int getR();

    //设置获取圆心
    void setCenter(Point center); //参数就是Point类创建对象center
    Point getCenter();
private:
    int m_R; //半径
    //另一个类, 作为这个类里的成员
    Point m_Center; //圆心
};

circle.cpp

//circle.cpp 圆类的实现

#include "circle.h"
//只需要留下函数的实现
//并且,每个函数前加上作用域Circle::
//shift + Tab统一缩进
//circle.cpp 圆类的实现

void Circle::setR(int r)
{
    m_R = r;
}
int Circle::getR() //这是一个成员函数
{
    return m_R;
}
//设置获取圆心
void Circle::setCenter(Point center) //参数就是Point类创建对象center
{
    m_Center = center;
}
Point Circle::getCenter()
{
    return m_Center; //返回Point类型的对象
}

🏆对象特性

🌳构造函数和析构函数

如果我们不提供构造和析构,编译器会自动提供其空实现,不需要我们调用,系统会自动调用

构造函数可以有参数,即可以重载
析构函数不能有参数,即不能重载

#include<iostream>
using namespace std;

//对象初始化和清理

//1,构造函数  初始化操作
class Person
{
public: //作用域
    //1, 构造函数
    //函数名 = 类名
    //构造函数可以有参数, 可以重载
    //创建对象时,会自动调用,而且只调用一次
    Person() //没有返回值, 不用void
    {
        cout<<"Person 构造函数调用"<<endl;
    }

    //2,析构函数  清理操作
    //没有返回值  不写void
    //函数名 = 类名 , 加~
    //析构函数无参数  不可重载
    //对象销毁前 自动调用析构函数  只调用一次
    ~Person()
    {
        cout<<"Person 析构函数调用"<<endl;
    }
};

//构造和析构都是必须有的实现  自己不提供  编译器会提供一个空实现的构造和析构
void test01()
{
    Person p; //栈上的数据,test01执行完毕后,释放这个对象
}

int main()
{
    test01(); //自动调用

    //Person p;

    system("pause");
    return 0;
}
Person 构造函数调用
Person 析构函数调用
请按任意键继续. . .

🌳函数的分类以及调用

#include<iostream>
using namespace std;

//1 构造函数的分类和调用
//分类
//  按参数分类 无参(默认)和有参构造
//  按类型分类 普通 和  拷贝 构造
class Person
{
public: //作用域
    //构造函数
    Person()
    {
        cout<<"Person 无参 构造函数调用"<<endl;
    }
    Person(int a)
    {
        age = a;
        cout<<"Person 有参 构造函数调用"<<endl;
    }
    //拷贝构造函数
    Person(const Person &p) //加const 不能将本身修改掉, 引用传入
    {
        //将传入人身上的所有属性, 拷贝到我身上
        age = p.age;
        cout<<"Person 拷贝 构造函数调用"<<endl;
    }
    ~Person() //自动调用
    {
        cout<<"Person 析构函数调用"<<endl;
    }
    int age;
};

//调用
void test01()
{
    //1,括号法
    Person p; //默认构造函数调用
    Person p2(10); //有参构造函数
    Person p3(p2); //拷贝构造函数

    //注意事项
    //调用默认构造函数时  不加()
    //Person p1(); //编译器认为是函数的声明, 没有创建对象, 类似void func();

    //cout<<"p2的年龄"<<p2.age<<endl;
    //cout<<"p3的年龄"<<p3.age<<endl;


    //2,显示法
    //Person p1; //默认构造
    //Person p2 = Person(10); //有参构造
    //Person p3 = Person(p2); //拷贝构造

    //Person(10); //匿名对象 特点:当前行执行结束后,系统会立即回收匿名对象
    //cout<<"aaaaa"<<endl;

    //注意事项2
    //不要利用拷贝构造函数  初始化匿名对象 编译器会认为Person (p3) === Person p3
    //Person(p3); //重定义

    //3,隐式转换
    //Person p4 = 10; //等价于 Person p4 = Person(10);
    //Person p5 = p4; //拷贝构造
}


int main()
{
    test01();

    system("pause");
    return 0;
}
Person 无参 构造函数调用
Person 有参 构造函数调用
Person 拷贝 构造函数调用
Person 析构函数调用
Person 析构函数调用
Person 析构函数调用
请按任意键继续. . .

🌳拷贝构造函数调用时机

1,使用一个已经创建完毕的对象来初始化一个新对象(复制克隆)
2,值传递给参数传值
3,值方式返回局部对象

#include<iostream>
using namespace std;

//拷贝构造函数调用时机

class Person
{
public:
    Person()
    {
        cout<<"Person 默认 构造函数调用"<<endl;
    }
    Person(int age) //有参构造
    {
        cout<<"Person 有参 构造函数调用"<<endl;
        m_Age = age;
    }
    ~Person()
    {
        cout<<"Person 析构 函数调用"<<endl;
    }

    Person(const Person & p) //拷贝构造函数
    {
        cout<<"Person 拷贝 构造函数调用"<<endl;
        m_Age = p.m_Age;
    }

    int m_Age; //属性
};

//1, 使用一个已经创建完毕对象来初始化一个新对象
void test01()
{
    Person p1(20);
    Person p2(p1); //p1拷贝到p2

    cout<<"P2的年龄:"<<p2.m_Age<<endl;
}

//2, 值传递给函数参数传值
void doWork(Person p) //值传递, 会拷贝出一个临时副本
{

}
void test02()
{
    Person p; //调用默认构造函数, 不需要()
    doWork(p);
}

//3, 值方式返回局部对象
Person doWork2() //return 的类型是Person, 这里要对应
{
    Person p1;
    cout<<(int*) &p1<<endl;
    return p1; //根据p1创建一个新的对象
}

void test03()
{
    Person p = doWork2(); //用Person类型的p变量来接收
    cout<<(int*) &p<<endl;
}

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

    system("pause");
    return 0;
}
Person 默认 构造函数调用
0x6dfecc
0x6dfecc
Person 析构 函数调用
请按任意键继续. . .

🌳构造函数调用规则

C++编译器给一个类添加3个函数:
1,默认构造函数(无参,函数体为空)
2,默认析构(无参,函数体空)
3,默认拷贝,对属性进行值拷贝

构造函数调用规则

1,用户定义有参构造函数,C++不提供默认无参构造,但会提供默认拷贝构造
2,用户定义拷贝构造函数,C++不再提供其他

有参√ --> 默认×  拷贝√                              拷贝√ --> 默认×  有参×

#include<iostream>
using namespace std;

//构造函数调用规则

//1,
//创建一个类, C++编译器给每个类添加至少3个函数
//默认构造, 析构函数(空实现)
//拷贝构造(值拷贝)

//2,
//如果写了有参构造函数, 编译器不再提供默认构造,但仍提供拷贝构造
//如果写了拷贝构造函数, 编译器不再提供其他普通构造,

class Person
{
public:
//    Person()
//    {
//        cout<<"Person 默认 狗杂啊"<<endl;
//    }
    Person(int age)
    {
        cout<<"Person 有参 狗杂啊"<<endl;
        m_Age = age;
    }
//    Person(const Person & p)
//    {
//        cout<<"Person 拷贝 狗杂啊"<<endl;
//        m_Age = p.m_Age;
//    }
    ~Person()
    {
        cout<<"Person 析构 狗杂啊"<<endl;
    }

    int m_Age; //属性
};

//void test01()
//{
//    Person p;
//    p.m_Age = 18;
//
//    Person p2(p);
//
//    cout<<"p2年龄为:"<<p2.m_Age<<endl;
//}

void test02()
{
    Person p(47);

    Person p2(p);
    cout<<"p2年龄为:"<<p2.m_Age<<endl;
}

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

    system("pause");
    return 0;
}
Person 有参 狗杂啊
p2年龄为:47
Person 析构 狗杂啊
Person 析构 狗杂啊
请按任意键继续. . .

🌳深拷贝与浅拷贝

面试常考

浅拷贝:简单的赋值拷贝操作
深拷贝:堆区重新申请空间,进行拷贝操作

浅拷贝的问题是, 堆区内存被重复释放,需要利用深拷贝解决(自己实现拷贝构造函数,解决浅拷贝带来的问题)

析构代码的用途:将堆区开放的内存释放干净

#include<iostream>
using namespace std;

//深拷贝与浅拷贝

class Person
{
public:
    Person()
    {
        cout<<"Person 默认"<<endl;
    }
    Person(int age, int height)
    {
        m_Age = age;
        //用new将数据创建在堆区
        m_Height = new int(height); //用指针接受堆区的数据
        cout<<"Person 有参"<<endl;
    }

    //自己实现拷贝构造函数  解决浅拷贝带来的问题
    Person(const Person &p)
    {
        cout<<"PErson 拷贝"<<endl;
        m_Age = p.m_Age;
        //m_Height = p.m_Height; //浅拷贝, 编译器默认实现

        m_Height = new int(*p.m_Height);
    }

    ~Person()
    {
        //析构代码: 将堆区开辟数据做释放操作
        if(m_Height != NULL) //指针不等于空
        {
            delete m_Height; //delete关键字释放内存
            m_Height = NULL; //防止野指针
        }
        cout<<"Person 析构"<<endl;
    }

    int m_Age; //年龄
    int *m_Height; //身高, 指针, 将身高数据开辟到堆区
};

void test01()
{
    Person p1(18, 160);
    cout<<"p1年龄为:"<<p1.m_Age<<" 身高为:"<<*p1.m_Height<<endl;

    Person p2(p1);
    cout<<"p2年龄为:"<<p2.m_Age<<" 身高为:"<<*p2.m_Height<<endl;
}

int main()
{
    test01();

    system("pause");
    return 0;
}
Person 有参
p1年龄为:18 身高为:160
PErson 拷贝
p2年龄为:18 身高为:160
Person 析构
Person 析构
请按任意键继续. . .

总结

如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题

🌳初始化列表

C++提供初始化列表语法,用来初始化属性

语法:
构造函数(): 属性1(值1), 属性2(值2), ...{}

#include<iostream>
using namespace std;

//初始化列表
class Person
{
public:


    //传统初始化操作
//    Person(int a, int b, int c)
//    {
//        m_A = a; m_B = b; m_C = c;
//    }
    //初始化列表初始化属性
    Person(int a, int b, int c): m_A(a), m_B(b), m_C(c)
    { //大括号里是实验题

    }
    int m_A, m_B, m_C;
};

void test01()
{
    //Person p(10, 20, 30);
    Person p(30, 20, 10);
    cout<<"m_A = "<<p.m_A<<endl;
    cout<<"m_B = "<<p.m_B<<endl;
    cout<<"m_C = "<<p.m_C<<endl;
}

int main()
{
    test01();

    system("pause");
    return 0;
}
m_A = 30
m_B = 20
m_C = 10
请按任意键继续. . .

🌳类对象作为类成员

C++中类的成员可以是另一个类的对象,称该成员为对象成员

构造时先构造类对象,再构造自身,析构时顺序相反 

#include<iostream>
using namespace std;

//类对象作为类成员

//手机类
class Phone
{
public:
    Phone(string pName) //构造函数,接受一个参数pName表示手机品牌名称
    {
        cout<<"Phone  构造  调用"<<endl;
        m_PName = pName;
    }
    ~Phone()
    {
        cout<<"Phone 析构"<<endl;
    }
    //手机品牌名称
    string m_PName; //phone_name

};

//人类
class Person
{
public:
    //Phone m_Phone = pName  隐式转换
    //构造函数, 接受两个参数, 通过接受的参数初始化
    Person(string name, string pName): m_Name(name), m_Phone(pName)
    {
        cout<<"Person 构造  调用"<<endl;
    }
    ~Person()
    {
        cout<<"Person 析构"<<endl;
    }
    //姓名
    string m_Name;
    //手机
    //类中的成员是另一个类的对象
    Phone m_Phone; //作为类成员保存

};

//当其他类作为本类成员,构造时先构造对象,再构造自身

void test01()
{
    Person p("张三", "苹果MAX"); //实例化一个Person对象,提供姓名和手机品牌名称
    cout<<p.m_Name<<"拿着"<<p.m_Phone.m_PName<<endl;
}


int main()
{
    test01();

    system("pause");
    return 0;
}
Phone  构造  调用
Person 构造  调用
张三拿着苹果MAX
Person 析构
Phone 析构
请按任意键继续. . .

🌳静态成员

静态成员,前加static关键字

静态成员变量:
1,所有对象共享一份数据
2,编译阶段分配内存
3,类内声明,类外初始化

静态成员变量

#include<iostream>
using namespace std;

//静态成员变量
class Person
{
public:

    //所有对象共享同一份数据
    //编译阶段分配内存
    //类内声明,类外初始化
    static int m_A; //类内声明

    //静态成员变量也是有访问权限的
private:
    static int m_B; //类内声明
};

//类外初始化
int Person::m_A = 100; //Person作用域下的成员
int Person::m_B = 200;

void test01()
{
    Person p;
    //100
    cout<<p.m_A<<endl;

    Person p2;
    p2.m_A = 200;
    //200
    cout<<p.m_A<<endl; //m_A是共享的属性, p2改了p.m_A也会变
}

void test02()
{
    //静态成员变量  不属于某个对象  所有对象共享同一份数据
    //so, 两种访问方式

    //1,对象去访问
//    Person p;
//    cout<<p.m_A<<endl;

    //2,类名去访问
    cout<<Person::m_A<<endl; //静态成员变量可直接通过类名访问

    //cout<<Person::m_B<<endl; //私有成员变量,类外无法访问
}


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

    system("pause");
    return 0;
}
100
请按任意键继续. . .

静态成员函数:
1,所有对象共享同一函数
2,静态成员函数只能访问静态成员变量 

静态成员函数

#include<iostream>
using namespace std;

//静态成员函数
//1,所有对象共享同一个函数
//静态成员函数只能访问静态成员变量

class Person
{
public:

    //静态成员函数
    static void func()
    {
        m_A = 100; //静态成员函数 可以访问静态成员变量
        //m_B = 200; //静态成员函数  不能访问非静态成员变量  无法区分具体是哪个的m_B属性
        cout<<"static void func调用"<<endl;
    }
    //类内声明
    static int m_A; //静态成员变量
    int m_B;

    //静态成员函数也是有访问权限的
private:
    static void func2()
    {
        cout<<"static void func2 调用"<<endl;
    }
};

int Person::m_A = 0; //类外初始化

void test01()
{
    //1,通过对象访问
    Person p;
    p.func();

    //2,通过类名访问
    Person::func();
    //Person::func2(); //类外无法访问私有静态成员函数
}

int main()
{
    test01();

    system("pause");
    return 0;
}
static void func调用
static void func调用
请按任意键继续. . .

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

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
    //C++编译器会给每个空对象也分配一个字节空间  为了区分空对象占内存的位置
    //每个空对象应该有一个独一无二的内存地址
    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;
}
size of p = 4
请按任意键继续. . .

🌳this指针的用途

概念
C++成员变量和成员函数是分开存储的
每一个非静态成员函数只会诞生一份函数实例,也就是多个同类型对象共用一块代码
那么,这一块代码,如何区分,哪个对象调用自己呢?

C++提供特殊的对象指针,this指针,解决上述问题

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

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

不需定义,可直接用

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

#include<iostream>
using namespace std;

class Person
{
public:
    Person(int age)
    {
        //this指针指向被调用的成员函数所属的对象
        this->age = age; //p1调用成员函数, this就指向p1
    }
    //定义一个成员函数
    Person& PersonAddAge(Person &p) //引用方式作为返回
    {
        this->age += p.age; //把Person &p的年龄加到自己this->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()函数, 但调用完一次后返回的是void
    //除非用this返回对象本身

    //链式编程思想
    p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);
    cout<<"p2年龄: "<<p2.age<<endl;
}


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

    system("pause");
    return 0;
}
p2年龄: 40
请按任意键继续. . .

🌳空指针访问成员函数

#include<iostream>
using namespace std;

//空指针调用成员函数
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; //指针, 类型是Person *, 指向空

    //p->showClassName();
    p->showPersonAge();
}


int main()
{
    test01();

    system("pause");
    return 0;
}

🌳const修饰成员函数

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

常对象
1,声明对象前加const
2,常对象只能调用常函数

🏆总结

个人觉得,跟着敲会稍微占多一点点时间,应该是利大于弊的

但是还是觉得太慢了,杂而且多,很多最终做项目应该用不上,但是了解下也没坏处

最终都是面向项目,进而面向面试就业的,不要在虚头八脑的概念花过多时间

无法落地  = 0,基础基本掌握的情况下,最后3~5个月背背八股,就能胜任大多数面试八股文了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/572578.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

js数组去重与循环对象

目录 一、数组对象去重 1.1、需要获取重复数据 1.2、直接过滤filterfindIndex 二、循环对象 三、多层数组对象过滤 一、数组对象去重 1.1、需要获取重复数据 let persons [{"name": "yzq","age": 20,"gender": true,"hei…

k8s配置资源管理|secret|configmap

k8s配置资源管理|secret|configmap 一 配置资源管理1 创建 Secret2 使用方式3 将 Secret 导出到环境变量中 二 ConfigMap1 Pod 中使用 ConfigMap2 Pod的创建3 用 ConfigMap 设置命令行参数4 通过数据卷插件使用ConfigMap 一 配置资源管理 //Secret Secret 是用来保存密码、tok…

2023年6月合肥/厦门/长春/深圳DAMA-CDGP数据治理专家认证报名

目前6月18日CDGA&CDGP考试目前开放的城市有&#xff1a;北京、上海、广州(满)、深圳、长沙、呼和浩特、杭州&#xff08;满&#xff09;、南京、济南&#xff08;满&#xff09;、成都、西安、武汉&#xff08;满&#xff09;、天津。 新增了武汉、天津这2个城市。另外合肥…

【Netty】Reactor 模型(十)

文章目录 前言一、传统服务的设计模型二、NIO 分发模型三、Reactor 模型3.1、Reactor 处理请求的流程3.2、Reactor 三种角色 四、单Reactor 单线程模型4.1、消息处理流程4.2、缺点 五、单Reactor 多线程模型5.1、消息处理流程5.2、缺点 六、主从Reactor 多线程模型6.1、Reactor…

Python的一些基础实操练习题

书接上文多看一眼多进步&#xff0c;python入门到放弃&#xff0c;是根据python的知识点的一些基础练习题&#xff0c;说了是基础练习题&#xff0c;基础练习题&#xff0c;基础练习题&#xff0c;水平高的就别看了&#xff0c;平高的就别看了&#xff0c;高的就别看了&#xf…

IP协议-服务类型字段

服务类型&#xff08;Type of Service&#xff09;字段是比较复杂的一个字段&#xff0c;该字段经过多次标准变更。 IPv4报文 一、最初标准&#xff08;RFC 791&#xff09; RFC 791定义TOS字段总共占用8bit&#xff0c;分为IP Precedence优先级&#xff08;3bit&#xff09;、…

Ansys Zemax | 如何将高斯光整形为平顶光

概要 本文展示了如何设计光束整形器将激光器产生的高斯分布的光转换为平顶分布的光输出。&#xff08;联系我们获取文章附件&#xff09; 介绍 光束整形光学元件可以将入射光的光强分布转换为其他特定的分布输出。最常见的例子就是将激光器产生的高斯分布的光转换为平顶&#x…

GMesh的Mesh操作面板介绍

GMesh操作面板介绍 Define 用于控制网格生成过程中各个单元的尺寸大小 “Size at points”选项允许您指定空间中某些点的尺寸大小。这些点可以是模型的几何结构中的点&#xff0c;也可以是在Gmsh中手动定义的点&#xff08;使用“Point”命令&#xff09;。在这种情况下&…

pycharm在终端运行时ps 不显示环境

如果下面显示的是ps ----- 而不是 则需要把这儿修改一下

2023年Java教学大纲!好程序员教你如何快速学会Java!

今天好程序员给大家分享一篇2023年的Java教学大纲&#xff0c;跟着这篇大纲学习&#xff0c;并且熟练掌握该技能&#xff0c;实习轻松月入过万不是梦&#xff01; 一、Java初级程序员必须要掌握的技能&#xff1a; Java基础知识控制声明面向对象的概念数组字符串异常处理输入/输…

Ubuntu22.04安装最新Eigen库

按道理&#xff1a;该方法适用所有Linux&#xff0c;适合安装多版本 本文采用源码 cmake的方法安装&#xff0c;故前置条件&#xff1a; 源码下载&#xff0c;官网下载或GitLab下载安装cmake&#xff08;没有安装cmake&#xff0c;也可以采用其他办法安装&#xff09; 官网下…

解决win无法删除多层嵌套文件夹

起因&#xff1a;昨天研究jpackage工具&#xff0c;不小心搞得一个文件夹里嵌套了好几百个文件夹&#xff0c;用win自己的删除删不掉&#xff0c;shiftdel直接删除也不行&#xff0c;直接弹窗删除错误&#xff1b; 后来用电脑管家下载了个“文件粉碎”&#xff0c;添加目录&am…

硬核机器学习知识点教学--(代码讲解)

用代码和实战讲解机器学习&#xff0c;零基础一样看得懂&#x1f44f;&#x1f3fb;&#x1f44f;&#x1f3fb;&#x1f44f;&#x1f3fb; 复习、学习、备战考试皆可用&#x1f44f;&#x1f3fb;&#x1f44f;&#x1f3fb;&#x1f44f;&#x1f3fb; 本系列持续更新中&a…

举个栗子~Tableau 技巧(254):学做圆形维诺图(Voronoi diagram)

关于维诺图 维诺图用于分析不同集合之间的交集和差集关系。在数据科学和统计学中&#xff0c;它常用于可视化不同数据集之间的重叠和交集&#xff0c;以便更好地理解它们之间的关系和差异。 例如&#xff0c;我们可以使用维诺图来展示不同客户群之间的交集&#xff0c;以便更…

编辑与校对的艺术:如何提高公文写作质量

在写作过程中&#xff0c;编辑与校对是提高作品质量的关键环节。它们不仅涉及语法、拼写和标点等基本问题&#xff0c;还包括文本的组织、表达和内容。通过掌握编辑与校对的艺术&#xff0c;你可以使你的文字更具说服力、更清晰易懂&#xff0c;从而更有效地传达你的观点。 1.认…

分布式系统监控zabbix安装部署及自定义监控

目录 一、zabbix的基本概述1.1 zabbix 监控原理1.2 Zabbix 6.0 新特性1.3 Zabbix 6.0 功能组件1.4 zabbix的监控对象1.5 zabbix的常用术语 二、zabbix进程详解三、zabbix的监控框架四、zabbix源码安装及部署4.1 部署 zabbix 服务端4.2 安装 zabbix 客户端&#xff0c;实现 zabb…

00后学什么技术有前途?2023年Java和前端发展前景分析!

00后的你还在想着进厂吗&#xff1f;每天在流水线上打螺丝&#xff0c;过着一成不变的日子&#xff0c;而且每个月就休息那么几天。如果你不想进厂&#xff0c;特别是对那些20岁刚出头或者学历不是那么有优势的年轻人&#xff0c;好程序员建议还是应该去学习一门技术&#xff0…

从 OceanBase 迁移数据到 DolphinDB

OceanBase 是一款金融级分布式关系数据库&#xff0c;具有数据强一致、高可用、高性能、在线扩展、高度兼容 SQL标准和主流关系数据库、低成本等特点&#xff0c;但是其学习成本较高&#xff0c;且缺乏金融计算函数以及流式增量计算的功能。 DolphinDB 是一款国产的高性能分布…

Micro-python Socket 支持 ROS2 topic 框架 (一)

消息Topic ROS2官方文档 Topic官方介绍 是各节点之间的信息交流媒介&#xff0c;可以实现一对一&#xff0c;一对多&#xff0c;多对一&#xff0c;多对多的信息交流&#xff0c;如图所示 &#xff08;一&#xff09;使用工具打开消息流图 打开rqt_graph&#xff08;注意其…

如何利用IDEA将Git分支代码回退到指定历史版本

一、背景 作为一名后端开发&#xff0c;相信大家一定遇到过这样的情景&#xff0c;代码开发人员过多&#xff0c;并且开发分支过多&#xff0c;导致代码版本管理困难&#xff0c;这样就难免遇到一些代码合并出错&#xff0c;比如&#xff0c;当我提交了本次修改到本地和远程分…