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