4.2.2 构造函数的分类及调用
分类:
。有参和无参(默认构造)
。普通和拷贝
拷贝构造函数写法
P(const person &P)
{
}
分类示例:
class pn
{
public:
//构造函数
pn()//无参
{
cout << "构造函数无参" << endl;
}
pn(int a)//有参
{
name = a;
cout << "构造函数有参" << endl;
}
//拷贝构造函数
pn(const pn &Person)
{
//将传入的人身上的所有属性,拷贝到我身上
// name = pn.name;
}
//析构函数 (与类同名!!!)
~pn()
{
cout << "析构函数" << endl;
}
int name;
};
调用:
。括号法
。显示法
。隐式转换法
注意:
。默认构造函数调用时不加括号
。不用拷贝来初始化对象
调用实例:
//括号
int a=10;
pn p1;//无参(默认)构造函数调用不要加括号,若加括号,编译器则判断为一个函数的声明 ,不会认为是创建对象
pn p2(a);
pn p3(p1);
cout << "输出p2的值为" << p2.name << endl;
cout << "输出p3的值为" << p3.name << endl;
//显示法
pn p4;//无参
pn p5 = pn(10);//有参
pn p6 = pn(p4);//拷贝
pn(10); //匿名对象 在执行完当前语句后自动回收匿名对象
//pn(p4); //不要用拷贝来初始化一个匿名对象 编译器认为pn p4 ==pn (p4),所以重复定义
//隐式转换法
pn p7 = 10; //相当于 pn p7 = pn(10);
pn p8 = p7;
分类及调用完整代码:
#include<iostream>
using namespace std;
class pn
{
public:
//构造函数
pn()//无参
{
cout << "构造函数无参" << endl;
}
pn(int a)//有参
{
name = a;
cout << "构造函数有参" << endl;
}
//拷贝构造函数
pn(const pn &Person)
{
//将传入的人身上的所有属性,拷贝到我身上
// name = pn.name;
}
//析构函数 (与类同名!!!)
~pn()
{
cout << "析构函数" << endl;
}
int name;
};
//调用
int main()
{
//括号
int a=10;
pn p1;//无参(默认)构造函数调用不要加括号,若加括号,编译器则判断为一个函数的声明 ,不会认为是创建对象
pn p2(a);
pn p3(p1);
cout << "输出p2的值为" << p2.name << endl;
cout << "输出p3的值为" << p3.name << endl;
//显示法
pn p4;//无参
pn p5 = pn(10);//有参
pn p6 = pn(p4);//拷贝
pn(10); //匿名对象 在执行完当前语句后自动回收匿名对象
// pn(p4); //不要用拷贝来初始化一个匿名对象 编译器认为pn p4 ==pn (p4),所以重复定义
//隐式转换法
pn p7 = 10; //相当于 pn p7 = pn(10);
pn p8 = p7;
system("pause");
return 0;
}
4.2.3 拷贝构造的作用
。使用一个创建完毕的对象来初始化一个新对象
。值传递方式给函数参数传值
。值方式返回局部变量
实例:
#include<iostream>
using namespace std;
class car
{
public:
car()
{
cout << "凯迪拉克" << endl;
}
car(int speed)
{
cout << "迈凯伦" << speed << endl;
}
car(const car &c)
{
speed = c.speed;
cout << "比亚迪" << endl;
}
~car()
{
cout << "bmw" << endl;
}
int speed;
};
//使用一个创建完毕的对象来初始化一个新对象
void test1()
{
car c1(10);
car c2(c1);
cout << "迈凯伦为" << c2.speed << endl;
}
//值传递方式给函数参数传值
void round(car c)
{
}
void test2()
{
car c;
round(c);
}
//值方式返回局部对象
car round2()
{
car c1;
cout << (int*)&c1 << endl;
return c1;//创建一个新对象返回
}
void test3()
{
car c = round2();
cout << (int*)&c << endl;//打印地址,验证对象为函数中创建的新对象
}
int main()
{
// test1();
// test2();
test3();
system("pause");
return 0;
}
4.2.4 构造函数调用规则
默认情况下,c++编译器至少给一个类添加3个函数
。默认构造函数(无参,空)
。默认析构函数(无参,空)
。默认拷贝构造函数,对属性进行拷贝
注意:
。如果自定义有参构造函数,c++不再提供默认无参,但会提供默认拷贝
。如果自定义拷贝,c++不会再提供其他构造函数
4.2.5 深浅拷贝
浅:简单的赋值拷贝
深:在堆区重新申请空间,进行拷贝操作
浅拷贝带来的问题就是堆区的重复释放
所以利用深拷贝让新对象与旧对象不指向同一块堆区空间
实例:
#include<iostream>
using namespace std;
class room
{
public:
room()
{
cout << "badroom(无参构造)" << endl;
}
room(int a , int Light)
{
space = a;
light = new int(Light);
cout << "livingroom(有参构造)" << endl;
}
~room()
{
//析构代码,将堆区开辟数据做释放操作
cout << "washroom(析构)" << endl;
}
room(const room &r)
{
cout << "newroom(拷贝构造)" << endl;
space = r.space;
//深拷贝操作
light = new int(*r.light);
}
int space;
int *light;
};
void test()
{
room r1(200,300);
cout << "room1 have" << r1.space << "采光为" << *r1.light << endl;
room r2(r1);
cout << "room2 have" << r2.space << "采光为" << *r2.light << endl;
}
int main()
{
test();
system("pause");
return 0;
}
4.2.6 初始化列表
话不多说,直接看码
#include<iostream>
using namespace std;
class shoes
{
public:
//传统办法
// shoes(int a , int b , int c)
// {
// color = a;
// size = b;
// grand = c;
// }
int color;
int size;
int grand;
//列表初始化
shoes(int a,int b,int c) :color(a) , size(b) , grand(c)
{
}
};
int main()
{
shoes s(10,45,20);
// shoes s;
cout << s.color << s.size << s.grand ;
system("pause");
return 0;
}
4.2.7 类对象作为类成员
如结构体,一个类也能是另一个类的成员(类似结构体嵌套)
4.2.8 静态成员
成员变量或函数前加上static,则为静态
静态成员变量:
。所有对象共享同一份数据
。编译阶段分配内存(全局区)
。类内声明,类外初始化(类内static + 数据类型 + 变量名//类外 数据类型 + 类名(作用域) +::+变量名+数据
静态成员函数:
。所有对象共享同一个函数
。静态成员函数只能访问静态成员变量!!!(函数体无法区分不同对象的成员变量)
4.3 C++对象模型和this指针
4.3.1 成员变量和函数的储存
。静态不占内存
。空对象占内存为1(为了区分空对象占内存的位置)
4.3.2 this指针
每一个非静态成员函数只会生成一份实例,也就是多个同类对象使用同一段代码,那这一份代码如何区分是哪个对象调用的自己呢?
可以使用this指针,解决上述问题
。this指针指向被调用的成员函数所属的对象
。this指针是一种隐含每一个非静态成员函数的指针
。无需声明,直接调用
。当形参和成员变量同名时,用this区分(一般情况下,类内名称可加上m_前缀表示成员变量或函数,用于区分)
class noodles
{
public:
noodles(string kind)
{
kind = kind;//报错,编译器认为三者为同一份
//解决方法:
this->kind = kind;//使用this指针区分
//m_kind = kind; //定义时区分
}
string kind;
// string m_kind;
};
//名称冲突
void test1()
{
noodles n1("lanzhou");
cout << "这是" << n1.kind << "面" << endl;
}
。在类的非静态成员函数中返回对象本身,可使用return *this
class noodles
{
public:
noodles(string kind)
{
kind = kind;//报错,编译器认为三者为同一份
//解决方法:
this->kind = kind;//使用this指针区分
//m_kind = kind; //定义时区分
}
void kindof(noodles &n)
{
this->kind += n.kind;
}
string kind;
// string m_kind;
};
//名称冲突
void test1()
{
noodles n1("lanzhou");
cout << "这是" << n1.kind << "面" << endl;
}
//返回对象本身
void test2()
{
noodles n1("lanzhou");
noodles n2("paomian");
n2.kindof(n1);//如果我需要叠加多次呢
//如下
n2.kindof(n1).kindof(n1).kindof(n1);//此时编译器报错,void返回对象导致无法叠加
cout << n2.kind <<endl;
}
int main()
{
test1();
test2();
system("pause");
return 0;
}
当需要叠加多次,则需使用this指针返回本体达到目的(注意返回类型需更改为引用方式返回)
#include<iostream>
#include<string>
using namespace std;
class noodles
{
public:
noodles(string kind)
{
kind = kind;//报错,编译器认为三者为同一份
//解决方法:
this->kind = kind;//使用this指针区分
//m_kind = kind; //定义时区分
}
noodles& kindof(noodles &n)
{
this->kind += n.kind;
//*this指向p2
return *this;
}
string kind;
// string m_kind;
};
//名称冲突
void test1()
{
noodles n1("lanzhou");
cout << "这是" << n1.kind << "面" << endl;
}
//返回对象本身
void test2()
{
noodles n1("lanzhou");
noodles n2("paomian");
n2.kindof(n1);//如果我需要叠加多次呢
//如下
n2.kindof(n1).kindof(n1).kindof(n1);//此时编译器报错,void返回对象导致无法叠加
cout << n2.kind <<endl;
}
int main()
{
test1();
test2();
system("pause");
return 0;
}
若返回类型不使用引用,而是值返回,那么每次返回相当于调用一次拷贝构造函数,结果也是无法叠加
4.3.3 空指针访问成员函数
#include<iostream>
using namespace std;
class People
{
public:
void showPeople()
{
cout << "this is People" << endl;
}
void shouweiht()
{
cout << "this weight" << m_weight << endl;
}
int m_weight;
};
void test1()
{
People *p = NULL;
p->shouweiht();
p->showPeople();
}
int main()
{
test1();
system("pause");
return 0;
}
报错,读取访问权限冲突,因为传入指针为空,所以在shouweight中m_weight没有实体
这种情况可采取以下操作
#include<iostream>
using namespace std;
class People
{
public:
void showPeople()
{
cout << "this is People" << endl;
}
void shouweiht()
{
if(this==NULL)return;
cout << "this weight" << m_weight << endl;
}
int m_weight;
};
void test1()
{
People *p = NULL;
p->shouweiht();
p->showPeople();
}
int main()
{
test1();
system("pause");
return 0;
}
加入判断,得以解决
4.4 友元
作用:让一个函数或者类,访问到另一个类中的私有成员
关键字:friend
友元的实现:
。全局函数做友元
#include<iostream>
#include<string>
using namespace std;
class Car
{
//告知编译器 boy全局函数是Car类的友元,可以访问私有内容
friend void boy(Car * car);
public:
Car()
{
this->m_car1 = "本田";
this->m_car2 = "法拉利";
}
public:
string m_car1;
private:
string m_car2;
};
void boy(Car * car)
{
cout << "好友正在开" << car->m_car1 << endl;
cout << "好友正在开" << car->m_car2 << endl;
}
void test()
{
Car c;
boy(&c);
}
int main()
{
test();
system("pause");
return 0;
}
。类做友元
#include<iostream>
#include<string>
using namespace std;
class goodgay
{
public:
goodgay();
void visit();
private:
Car * car;
};
class Car
{
//告知编译器 boy全局函数是Car类的友元,可以访问私有内容
friend void boy(Car * car);
friend class goodgay;//类做友元
public:
Car()
{
this->m_car1 = "本田";
this->m_car2 = "法拉利";
}
public:
string m_car1;
private:
string m_car2;
};
void boy(Car * car)
{
cout << "好友正在开" << car->m_car1 << endl;
cout << "好友正在开" << car->m_car2 << endl;
}
goodgay::goodgay()
{
car = new Car;
}
//类外写成员函数
void goodgay::visit()
{
cout << "好友正在开" << car->m_car1 << endl;
cout << "好友正在开" << car->m_car2 << endl;
}
void test()
{
// Car c;
// boy(&c);
goodgay g;
g.visit();
}
int main()
{
test();
system("pause");
return 0;
}
。成员函数做友元
与上相同,使用friend关键字声明即可实现可访问
4.5 运算符重载!!!(重要)
对已有的运算符进行重新定义,赋予其新的功能,以适应不同的数据类型
4.5.1 加号运算符重载
作用:实现两个自定义数据类型的相加
成员函数实现
#include <iostream>
using namespace std;
class Number {
private:
int num;
public:
Number(int n) : num(n) {}
// 1. 成员函数重载+
Number operator+(const Number& other) {
Number result(0);
result.num = this->num + other.num;
return result;
}
void display() {
cout << "Number: " << num << endl;
}
};
int main() {
Number num1(5);
Number num2(10);
Number sum = num1 + num2; // 使用重载的+号进行计算
sum.display(); // 输出结果
return 0;
}
全局函数实现
#include <iostream>
using namespace std;
class Number {
private:
int num;
public:
Number(int n) : num(n) {}
void display() {
cout << "Number: " << num << endl;
}
// 2. 全局函数重载+
friend Number operator+(const Number& num1, const Number& num2) {
Number result(0);
result.num = num1.num + num2.num;
return result;
}
};
int main() {
Number num1(5);
Number num2(10);
Number sum = num1 + num2; // 使用重载的+号进行计算
sum.display(); // 输出结果
return 0;
}
4.5.2 左移运算符重载
作用:实现输出自定义数据类型
成员函数实现
#include <iostream>
using namespace std;
class MyData {
private:
int value1;
int value2;
public:
MyData(int v1, int v2) : value1(v1), value2(v2) {}
// 1. 成员函数重载<<
friend ostream& operator<<(ostream& os, const MyData& data) {
os << "Custom Data: " << data.value1 << ", " << data.value2;
return os;
}
};
int main() {
MyData data(10, 20);
cout << data; // 使用重载的<<输出自定义数据类型
return 0;
}
全局函数实现
#include <iostream>
using namespace std;
class MyData {
private:
int value1;
int value2;
public:
MyData(int v1, int v2) : value1(v1), value2(v2) {}
friend ostream& operator<<(ostream& os, const MyData& data);
};
// 2. 全局函数重载<<
ostream& operator<<(ostream& os, const MyData& data) {
os << "Custom Data: " << data.value1 << ", " << data.value2;
return os;
}
int main() {
MyData data(10, 20);
cout << data; // 使用重载的<<输出自定义数据类型
return 0;
}
4.5.3 递增运算符重载
作用:通过重载运算符,实现自己的整型数据
成员函数实现
#include <iostream>
using namespace std;
class MyInt {
private:
int value;
public:
MyInt(int v) : value(v) {}
// 1. 成员函数重载++
MyInt& operator++() {
value = value + 1;
return *this;
}
void display() {
cout << "Value: " << value << endl;
}
};
int main() {
MyInt num(5);
++num; // 使用重载的递增运算符
num.display(); // 输出结果
return 0;
}
全局函数实现
#include <iostream>
using namespace std;
class MyInt {
private:
int value;
public:
MyInt(int v) : value(v) {}
void display() {
cout << "Value: " << value << endl;
}
// 2. 全局函数重载++
friend MyInt& operator++(MyInt& num) {
num.value = num.value + 1;
return num;
}
};
int main() {
MyInt num(5);
++num; // 使用重载的递增运算符
num.display(); // 输出结果
return 0;
}
4.5.4 关系运算符重载
作用:使其通过重载关系运算符,实现两个自定义类型的对象进行对比操作
成员函数实现
#include <iostream>
using namespace std;
class MyData {
private:
int value;
public:
MyData(int v) : value(v) {}
// 1. 成员函数重载==
bool operator==(const MyData& other) {
return this->value == other.value;
}
};
int main() {
MyData data1(10);
MyData data2(20);
MyData data3(10);
if (data1 == data2) {
cout << "data1 is equal to data2" << endl;
} else {
cout << "data1 is not equal to data2" << endl;
}
if (data1 == data3) {
cout << "data1 is equal to data3" << endl;
} else {
cout << "data1 is not equal to data3" << endl;
}
return 0;
}
全局函数实现
#include <iostream>
using namespace std;
class MyData {
private:
int value;
public:
MyData(int v) : value(v) {}
int getValue() const {
return value;
}
};
// 2. 全局函数重载==
bool operator==(const MyData& data1, const MyData& data2) {
return data1.getValue() == data2.getValue();
}
int main() {
MyData data1(10);
MyData data2(20);
MyData data3(10);
if (data1 == data2) {
cout << "data1 is equal to data2" << endl;
} else {
cout << "data1 is not equal to data2" << endl;
}
if (data1 == data3) {
cout << "data1 is equal to data3" << endl;
} else {
cout << "data1 is not equal to data3" << endl;
}
return 0;
}
4.6 继承
当下一级类拥有上一级类的特性,并且拥有自己的特性时,可考虑使用继承,减少代码量
4.6.1 继承的基本语法
// 基类
class Base {
public:
int baseVar;
void baseMethod() {
cout << "Base Method" << endl;
}
};
// 派生类
class Derived : public Base {
public:
int derivedVar;
void derivedMethod() {
cout << "Derived Method" << endl;
}
};
4.6.2.继承方式
C++支持公有、保护和私有继承方式,通过访问权限修饰符指定:
// 公有继承
class Derived : public Base {
// ...
};
// 保护继承
class Derived : protected Base {
// ...
};
// 私有继承
class Derived : private Base {
// ...
};
4.6. 3.继承中的对象模型
在继承中,派生类对象包含了基类的子对象。派生类对象中包含了基类的成员变量和成员函数。
4.6.4 继承中构造和析构顺序
在继承中,先调用基类的构造函数,再调用派生类的构造函数;析构的顺序与构造相反,先调用派生类的析构函数,再调用基类的析构函数。
#include <iostream>
using namespace std;
class Base {
public:
Base() {
cout << "Base Constructor" << endl;
}
~Base() {
cout << "Base Destructor" << endl;
}
};
class Derived : public Base {
public:
Derived() {
cout << "Derived Constructor" << endl;
}
~Derived() {
cout << "Derived Destructor" << endl;
}
};
int main() {
Derived d;
return 0;
}
4.6.5 继承同名成员的处理方式
在继承中,如果派生类中定义了与基类同名的成员函数或变量,会隐藏基类中的同名成员。如果要访问被隐藏的同名成员,可以使用作用域解析运算符`::`
#include <iostream>
using namespace std;
class Base {
public:
void display() {
cout << "Base Display" << endl;
}
};
class Derived : public Base {
public:
void display() {
cout << "Derived Display" << endl;
}
};
int main() {
Derived d;
d.display(); // 调用派生类的display
d.Base::display(); // 调用基类的display
return 0;
}
4.6.6 继承同名静态成员处理方式
静态成员在继承中是共享的,派生类可以直接访问基类的静态成员,而不会进行隐藏。
#include <iostream>
using namespace std;
class Base {
public:
static int value;
};
int Base::value = 5;
class Derived : public Base {
public:
void display() {
cout << "Value: " << value << endl; // 直接访问基类的静态成员
}
};
int main() {
Derived d;
d.display();
return 0;
}
4.6.7 多继承语法
class Base1 {
// ...
};
class Base2 {
// ...
};
class Derived : public Base1, public Base2 {
// ...
};
4.6.8 菱形继承
菱形继承指的是一个派生类同时继承自两个直接或间接基类,而这两个基类又继承自同一个基类。这种情况可能引起二义性和资源浪费,可以通过虚拟继承来解决。
#include <iostream>
using namespace std;
class A {
public:
int value;
};
class B : public A {
// ...
};
class C : public A {
// ...
};
class D : public B, public C {
// ...
};
int main() {
D d;
d.B::value = 5; // 指定访问B类的value
d.C::value = 10; // 指定访问C类的value
cout << "Value from B: " << d.B::value << endl;
cout << "Value from C: " << d.C::value << endl;
return 0;
}
4.7 多态
。多态分为两类
。静态多态:函数重载和 运算符重载属于静态多态,复用函数名。
。动态多态: 派生类和虚函数实现
运行时多态静态多态和动态多态区别:
。静态多态的函数地址早绑定 - 编译阶段确定函数地址
。动态多态的函数地址晚绑定 - 运行阶段确定函数地址
4.7.1 多态的基本语法
多态是指通过基类的指针或引用调用派生类的成员函数,实现不同对象以统一的方式响应消息的特性。在C++中,多态通过虚函数实现。
class Base {
public:
virtual void display() {
cout << "Display from Base" << endl;
}
};
class Derived : public Base {
public:
void display() override {
cout << "Display from Derived" << endl;
}
4.7.2 纯虚函数和抽象类
纯虚函数是在基类中声明的虚函数,但没有给出具体实现,其目的是让派生类去实现该函数。包含纯虚函数的类称为抽象类,无法直接实例化对象。
class AbBase {
public:
virtual void pVFun() = 0; // 纯虚函数
};
class Derived : public AbBase {
public:
void pVlF() override {
cout << "Derived 应用 pvfun" << endl;
}
};
4.7.3 虚析构和纯虚析构
虚析构函数用于通过基类指针删除派生类对象时,确保正确调用派生类的析构函数。纯虚析构函数是一个没有函数体的虚析构函数,用于定义一个纯虚基类。
class Base {
public:
virtual ~Base() {
cout << "Base 析构" << endl;
}
};
class AbsBase {
public:
virtual ~AbBase() = 0; // 纯虚析构函数声明
};
AbBase::~AbBase() {} // 纯虚析构函数定义
5 文件操作
程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放通过文件可以将数据持久化
C++中对文件操作需要包含头文件 < fstream >
文件类型分为两种:
1.文本文件- 文件以文本的ASCII码形式存储在计算机中2.二进制文件 文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂它们
操作文件的三大类:
1.ofstream: 写操作
2.ifstream: 读操作
3. fstream :读写操作
5. 1. 文本文件
5.1.1 写文件
写文件步骤如下
1.包含头文件
#include <fstream>
2.创建流对象
ofstream ofs;
3.打开文件
ofs.open("文件路径",打开方式);
4.写数据
ofs <<"写入的数据”
5.关闭文件
ofs.close();
文件打开方式
实例:
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ofstream outFile("textfile.txt"); // 创建输出文件流对象
if (outFile.is_open()) {
outFile << "这是一个文件" << endl;
outFile << "能写" << endl;
outFile.close(); // 关闭文件
cout << " successfully." << endl;
} else {
cout << "Unable open ." << endl;
}
return 0;
}
5.1.2 读文件
读文件步骤如下:
1.包含头文件
#include <fstream>
2.创建流对象
ifstream ifs;
3.打开文件并判断文件是否打开成功ifs.open("文件路径",打开方式):
4.读数据
四种方式读取
5.关闭文件
ifs.close();
实例:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main() {
ifstream inFile("textfile.txt"); // 创建输入文件流对象
string line;
if (inFile.is_open()) {
while (getline(inFile, line)) {
cout << line << endl; // 逐行读取并输出内容
}
inFile.close(); // 关闭文件
} else {
cout << "Unable open " << endl;
}
return 0;
}
5. 2 二进制文件
打开方式要指定为ios::binary
5.2.1 写文件
二进制方式写文件主要利用流对象调用成员函数write
函数原型 :ostream& write(const char * buffer,int len);
参数解释: 字符指针buffer指向内存中一段存储空间。len是读写的字节数
实例:
#include <iostream>
#include <fstream>
using namespace std;
struct Record {
int id;
char name[20];
double salary;
};
int main() {
ofstream outFile("binaryfile.dat", ios::binary); // 创建二进制输出文件流对象
if (outFile.is_open()) {
Record rec = {1, "彭于晏", 3500.50};
outFile.write(reinterpret_cast<char*>(&rec), sizeof(rec)); // 写入结构体数据
outFile.close(); // 关闭文件
cout << "successfully." << endl;
} else {
cout << "Unable open ." << endl;
}
return 0;
}
5.2.2 读文件
二进制方式读文件主要利用流对象调用成员函数read
函数原型: istream& read(char *buffer,int len);
参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数
实例:
#include <iostream>
#include <fstream>
using namespace std;
struct Record {
int id;
char name[20];
double salary;
};
int main() {
ifstream inFile("binaryfile.dat", ios::binary); // 创建二进制输入文件流对象
Record rec;
if (inFile.is_open()) {
inFile.read(reinterpret_cast<char*>(&rec), sizeof(rec)); // 读取结构体数据
cout << "ID: " << rec.id << endl;
cout << "名: " << rec.name << endl;
cout << "薪资: " << rec.salary << endl;
inFile.close(); // 关闭文件
} else {
cout << "Unable open ." << endl;
}
return 0;
}
以上贴图及概念有部分来自b站黑马程序员
【黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难】https://www.bilibili.com/video/BV1et411b73Z?p=146&vd_source=1a0f1d0385352c94160379ddb0f8f7a1
这周的笔记先到这里
谢谢观看