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

👂 快乐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个月背背八股,就能胜任大多数面试八股文了

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

19-01 技术选型的道与术

系列目录导航&#x1f449; 什么是技术选型&#xff0c;技术选型的重要性 根据实际业务管理的需要&#xff0c;对硬件、软件以及所要用到的技术进行规格的选择狭义上的技术选型&#xff1a;团队决定选用哪种技术去解决问题&#xff0c;比如选用某个技术语言、某个技术框架去开…

人民大学与加拿大女王金融硕士项目——在现在憧憬美好的未来

未来是一个虚无缥缈的词汇&#xff0c;抓不住也看不到。未来里有着我们无限的希望&#xff0c;也有着美好的憧憬。未来究竟是怎样的呢&#xff0c;有人说现在的样子里藏着未来的模样。在职的你有没有为未来编织一副美丽的画卷呢&#xff1f;未来很远&#xff0c;远到只能靠想象…

Linux设备树:删除节点和属性的方法

[摘要]&#xff1a;本文主要介绍了在设备树中删除节点&#xff08;node&#xff09;和属性&#xff08;property&#xff09;的方法。为了便于理解&#xff0c;笔者先介绍了 dtsi 和 dts 的关系&#xff0c;然后构建了虚拟的需求场景&#xff0c;最终给出示例。 背景知识 设备…

shell 免交互

文章目录 Here Document 免交互实验多行注释基本命令 实验 Here Document 免交互 使用I/O重定向的方式将命令列表提供给交互式程序或命令&#xff0c;比如 ftp、cat 或 read 命令。是标准输入的一种替代品可以帮助脚本开发人员不必使用临时文件来构建输入信息&#xff0c;而是…

理光打印机连接电脑后不打印的原因及解决方法

理光打印机在使用时&#xff0c;可能会出现正常连接上理光打印机却没有反应的情况&#xff0c;出现无法打印的情况&#xff0c;下面&#xff0c;驱动人生为大家带来理光打印机连接后不打印的解决方案。 驱动人生分析&#xff0c;一般遇到理光打印机连接后不打印的情况&#xf…

python 的APScheduler配置的定时任务会被Miss掉

背景 python 的APScheduler配置的定时任务会被Miss掉&#xff0c;经常在控制台收到一些Miss的告警信息&#xff0c;就觉得是任务太多导致的&#xff0c;为了定位到具体的原因&#xff0c;看了一些源码&#xff0c;了解到了定时任务的6大模块的协同工作模式。异常信息及来源 异…

2022年深圳杯数学建模B题基于用电可靠性的配电网规划解题全过程文档及程序

2022年深圳杯数学建模 B题 基于用电可靠性的配电网规划 原题再现&#xff1a; 如果一批用户变压器&#xff08;下面简称用户&#xff09;仅由一个电源变电站&#xff08;下面简称电源&#xff09;供电&#xff0c;称为单供。这时配电网由电线和开关联接成以电源为根节点的树状…

Transformer应用之构建聊天机器人(二)

四、模型训练解析 在PyTorch提供的“Chatbot Tutorial”中&#xff0c;关于训练提到了2个小技巧&#xff1a; 使用”teacher forcing”模式&#xff0c;通过设置参数“teacher_forcing_ratio”来决定是否需要使用当前标签词汇来作为decoder的下一个输入&#xff0c;而不是把d…

< ElementUi组件库: el-progress 进度条Bug及样式调整 >

ElementUi组件库&#xff1a; el-progress 进度条Bug及样式调整 &#x1f449; 前言&#x1f449; 一、实现原理> 修改 el-progress 进度条样式 及 渐变进度条样式 &#x1f449; 二、案例代码&#xff08;前言效果图案例&#xff09;> HTML代码> CSS代码 &#x1f44…

C++学习day--12 循环的应用,暴力破解密码和输出动图

第 1 节 职场修炼&#xff1a;程序员到底能干多久 现状&#xff1a; 很多程序员&#xff0c;过了 30 岁&#xff0c;纷纷转行。 原因&#xff1a; 1 &#xff09;薪资过万后&#xff0c;很难进一步提升 2 &#xff09;可替代性高&#xff0c;在新人面前&#xff0c;没有…

SolVES模型在生态系统服务社会价值评估中的运用

SolVES模型&#xff08;Social Values for Ecosystem Services&#xff09;全称为生态系统服务社会价值模型&#xff0c;是由美国地质勘探局和美国科罗拉多州立大学联合开发的一款地理信息系统应用程序&#xff0c;开发该模型的目的主要是对生态系统服务功能中的社会价值进行空…

全面了解Java连接MySQL的基础知识,快速实现数据交互

全面了解Java连接MySQL的基础知识&#xff0c;快速实现数据交互 1. 数据库的重要性2. MySQL数据库简介2.1 MySQL数据库的基本概念2.2 MySQL的基本组成部分包括服务器、客户端和存储引擎。2.3 安装MySQL数据库2.3.1安装MySQL数据库2.3.2 下载MySQL安装程序2.3.3 运行MySQL安装程…

帽子设计作品——蒸汽朋克的乌托邦,机械配件的幻想世界!

蒸汽朋克是由蒸汽steam和朋克punk两个词组成&#xff0c; 蒸汽代表着以蒸汽机作为动力的大型机械&#xff0c;而朋克则代表一种反抗、叛逆的精神。 蒸汽朋克的作品通常以蒸汽时代为背景&#xff0c;通过如新能源、新机械、新材料、新交通工具等新技术&#xff0c;使画面充满想…

理解深度可分离卷积

1、常规卷积 常规卷积中&#xff0c;连接的上一层一般具有多个通道&#xff08;这里假设为n个通道&#xff09;&#xff0c;因此在做卷积时&#xff0c;一个滤波器&#xff08;filter&#xff09;必须具有n个卷积核&#xff08;kernel&#xff09;来与之对应。一个滤波器完成一…

PMP课堂模拟题目及解析(第13期)

121. 项目经理、团队成员以及若干干系人共同参与一次风险研讨会。已经根据风险管理计划生成并提供一份风险报告。若要为各个项目风险进行优先级排序&#xff0c;现在必须执行哪一项分析&#xff1f; A. 定量风险分析 B. 根本原因分析 C. 偏差分析 D. 定性风险分析 122. …

带你手撕链式二叉树—【C语言】

前言&#xff1a; 普通二叉树的增删查改没有意义&#xff1f;那我们为什么要先学习普通二叉树呢&#xff1f; 给出以下两点理由&#xff1a; 1.为后面学习更加复杂的二叉树打基础。&#xff08;搜索二叉树、ALV树、红黑树、B树系列—多叉平衡搜索树&#xff09; 2.有很多二叉树…

Linux安装MongoDB数据库并内网穿透在外远程访问

文章目录 前言1.配置Mongodb源2.安装MongoDB数据库3.局域网连接测试4.安装cpolar内网穿透5.配置公网访问地址6.公网远程连接7.固定连接公网地址8.使用固定公网地址连接 转发自CSDN cpolarlisa的文章&#xff1a;Linux服务器安装部署MongoDB数据库 - 无公网IP远程连接「内网穿透…

亚马逊开放个人卖家验证入口?亚马逊卖家验证到底怎么搞?

亚马逊卖家账户的安全对于所有卖家来说都非常重要。如果卖家想要在亚马逊上长期稳定地发展&#xff0c;赚取更多的钱并推出更多热卖产品&#xff0c;就必须确保他们的亚马逊卖家账户安全&#xff0c;特别是一直存在的亚马逊账户验证问题。 近期&#xff0c;根据亚马逊官方披露的…

开发敏捷高效 | 云原生应用开发与运维新范式

5 月 18 日&#xff0c;腾讯云举办了 Techo Day 腾讯技术开放日&#xff0c;以「开箱吧&#xff01;腾讯云」为栏目&#xff0c;对外发布和升级了腾讯自研的一系列云原生产品和工具。其中&#xff0c;腾讯云开发者产品中心总经理刘毅围绕“开发敏捷高效”这一话题&#xff0c;分…

单体项目偶遇并发漏洞!短短一夜时间竟让老板蒸发197.83元

事先声明&#xff1a;以下故事基于真实事件而改编&#xff0c;如有雷同&#xff0c;纯属巧合~ 眼下这位正襟危坐的男子&#xff0c;名为小竹&#xff0c;他正是本次事件的主人公&#xff0c;也即将成为熊猫集团的被告&#xff0c;嗯&#xff1f;这究竟怎么一回事&#xff1f;欲…