本笔记为文本在Bilibil上学习c++,便于提高学习效率和以后复习来用
本章是本人在学习C++的知识之前,对一些忘记的知识进行补充学习,不建议大家看。引文差不多断断续续学了半年多的时间,所以写的毫无逻辑,又臭又长/(ㄒoㄒ)/~~
1、基础知识
1.1 sizeof关键字
可以计算某变量名所占用的内存空间(所占字节)
1.2.“short” “int” “long” "long long"内存空间比较
short占两个字节
int占四个字节
long占八个字节
long long占16个字节
1.3 C++风格的字符串
c++定义字符串可以这么定义:string str1 = “hellow wordd”;
前提是增加一个头文件 #include
c语言风格字符串只能这么定义:char str2[] = “hello world”;
1.4 bool类型
布尔类型的定义是这样的:bool flag = true;
布尔类型所占的空间为1个字节
布尔类型的值只有true和flase,非零的值为true,0为flase
1.5数据的输入
cin >> a;
class和struct的区别
struct默认权限是公共public
class默认权限是私有private
程序的内存模型
内存共分为四个区,每个区的存储释放的时间不同,可以使编程更加的灵活
2.1代码区
代码区是共享的,内存空间只保留一份代码
代码区是只读的,防止程序意外修改命令
该区域的数据在程序运行结束后,由操作系统自动释放
2.2全局区
全局区用来存放静态变量、常量、全局变量
该区域的数据在程序运行结束后,由操作系统自动释放
2.3栈区
由编译器自动分配释放,存放函数的参数值、局部变量等
注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放。
2.4 堆区
由程序员分配内存释放。程序结束由操作系统回收
在C++中,主要用new在堆区开辟内容
2.5 new操作符
c++中用new操作符在堆区开辟数据
堆区的数据由程序员手动开辟、释放
释放用delete
利用new创建的数据,会返回对应数据类型的指针
3成员属性设置为私有
1.可以自己控制读写的权限
2.可以检测数据的有效性
设置成员的例子:
#include<iostream>
using namespace std;
#include<string>
class Person
{
public:
void setName(string name)
{
Name = name;
}
string getName()
{
return Name;
}
int getAge()
{
age = 23;
return age;
}
void setWife(string name)
{
wife = name;
}
private:
string Name;
int age;
string wife;
};
int main()
{
Person p1;
p1.setName("张zeng洋");
p1.setWife("苍井");
cout << "姓名为:" << p1.getName() << endl;
cout << "年龄为:" << p1.getAge() << endl;
system("pause");
return 0;
}
3.1小练习
#include<iostream>
using namespace std;
#include<string>
class cube
{
private:
int L_cube, W_cube, H_cube;
public:
void set_L(int L)
{
L_cube = L;
}
void set_W(int L)
{
W_cube = L;
}
void set_H(int L)
{
H_cube = L;
}
int getL()
{
return L_cube;
}
int getH()
{
return H_cube;
}
int getW()
{
return W_cube;
}
int volume_cube()
{
return L_cube * W_cube * H_cube;
}
/* string isSameByClass(cube& c2)
{
if (L_cube == c2.getL() && H_cube == c2.getH() && W_cube == c2.getW())
return "相同";
else
return "不同";
}
*/
string isSameByClass(cube& c1, cube& c2)
{
if (c1.getL() == c2.getL() && c1.getH() == c2.getH() && c1.getW() == c2.getW())
return "相同";
else
return "不同";
}
};
//全局变量判断立方体是否相同
string isSame(cube& c1, cube& c2)
{
if ( c1.getL() == c2.getL() && c1.getH() == c2.getH() && c1.getW() == c2.getW())
return "相同";
else
return "不同";
}
int main()
{
cube c1,c2;
c1.set_W(2);
c1.set_H(3);
c1.set_L(4);
c2.set_W(2);
c2.set_H(3);
c2.set_L(3);
cout << "两个方体大小是否相等:" << isSame(c1, c2) << endl;
cout << "长方体面积为:" << c1.volume_cube() << endl;
cout << "两个方体大小是否相等成员函数:" << c1.isSameByClass(c1,c2) << endl;
return 0;
}
4 构造和析构函数
构造函数: 类名(){}
特点:
1.没有返回值,不写void
2.函数名与类名相同
3.构造函数可以有参数,可以重载
4.程序在调用对象的时候会自动调用构造,无需调用构造且只会调用一次
析构函数 ~类名(){}
1.析构函数,没有返回值也不写void
2.函数名称与类名相同,在名称前面加上符号~
3.析构函数不可以有参数,因此不可以发生重载
4.程序在对象销毁前自动调用析构函数,无需动手调用,而且只会调用一次
#include<iostream>
using namespace std;
class Person
{
public:
Person()
{
cout << "Person开始构造" << endl;
}
~Person()
{
cout << "Person开始析构" << endl;
}
};
int main()
{
Person P0;
return 0;
}
4.1 构造函数的分类及调用
4.1.1 分类
1.按照参数分类 无参构造(默认构造)和 有参构造
2.按照类型分类 普通构造 拷贝构造
//参数类型分类
class Person
{
public:
Person()
{
cout<<"无参构造函数调用"<<endl;
}
Person(int a)
{
cout<<"有参构造函数调用"<<endl;
age = a
}
//拷贝函数构造
Person(const Person &p)
{
//将传入的人身上的所有属性,拷贝到我身上
age=p.age
}
}
4.1.2 调用
1.括号法
Person p1;//默认构造函数调用,不要加小括号
Person p2(10);//有参构造函数调用
Person p3(p2);//拷贝构造函数调用
2.显示法
Person p1;
Person p2 = Person(10); //有参构造
Person p3 = Person(p2); //拷贝构造
Person(10);//匿名对象 特点,当前行结束后,系统会立即回收掉这个对象
//注意:不要利用拷贝构造函数 初始化匿名对象
3.隐式转换法
Person p4 = 10; //显示法的精简类型,跟Person p4 =Person(10)等价
Person p5=p4;//拷贝法的隐式转化法
4.1.3
拷贝构造函数调用时机:
1.使一个已经创建完毕的对象来初始化一个新对象
2.值传递的方式
4.1.4 构造函数的调用规则
1.默认情况下,c++编译器至少会给一个类添加三个函数
2.当在类中构造了有参函数,那么编译器不会生成默认构造函数,但会生成一个拷贝函数
3.当在类中构造了拷贝函数,则编译器不会自动生成有参函数和默认函数
4.1.5深拷贝和浅拷贝
浅拷贝:单纯赋值个名称,相当于A=B,
深拷贝:重新申请个空间进行拷贝
浅拷贝带来的最主要的问题是内存被重复释放
4.3.6初始化列表
初始化列表的主要目的是为了方便阅读(虽然我觉得不方便,哈哈)
但是能够精炼代码,减少代码量
#include<iostream>
using namespace std;
class Person
{
public:
Person(int a, int b, int c) :A(a), B(b), C(c) {}
void PrintPerson()
{
cout << "A:" << A << endl;
cout << "B:" << B << endl;
cout << "C:" << C << endl;
}
private:
int A, B, C;
};
/*{
public:
Person(int a, int b, int c)
{
A = a;
B = b;
C = c;
}
int A;
int B;
int C;
};
void test01()
{
Person p(10, 20, 30);
cout << "A:" << p.A << endl;
cout << "B:" << p.B << endl;
cout << "C:" << p.C << endl;
}*/
int main()
{
Person p(1, 2, 3);
p.PrintPerson();
system("pause");
return 0;
}
4.2静态成员
4.2.1静态成员变量
静态成员的主要实现方法是再成员变量和成员函数前面加上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 = 10;
int Person::m_B = 10;
void test01()
{
//静态成员变量的两种访问方式
Person p1;
Person p2;
p1.m_A = 100;
cout << "p1.m_A=" << p1.m_A << endl;
cout << "p2.m_A=" << p2.m_A << endl;//类的成员共享同一份数据
Person::m_A = 200;
cout <<"Person::m_A = " << Person::m_A << endl;
}
int main()
{
test01();
return 0;
}
4.2.2 静态成员函数
静态成员函数和静态成员变量大同小异
定义方式是在类内的函数前+static
特点:静态成员函数只能访问静态成员变量
4.3 this指针
4.3.1 类的成员内存分配
int m_A; //非静态成员变量,属于类的对象
static int m_B; //静态成员变量,不属于类的对象上
void func() {} //非静态成员函数,不属于类的对象上
static void func2() {} //静态成员函数,不属于类的对象上
4.3.2 this指针概念
this 指针,指向被调用成员函数所属的对象(也就是说只有非静态成员函数属于类的对象)。
this指针有两个作用:
1.当形参和成员变量同名是,可用this指针来区分开
2.在类的非静态成员函数中返回对象本身,可以使用return *this
#include<iostream>
using namespace std;
class Person
{
public:
Person(int age)
{
this->age = age;
}
Person& PersonAddPerson(Person p)//指针放在这是什意思
{
this->age += p.age;
return *this;
}
int age;
};
void test01()
{
Person p1(10);
cout << "p1.age=" << p1.age << endl;
Person p2(10);
p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);
cout << "p2.age=" << p2.age << endl;
}
int main()
{
test01();
return 0;
}
4.3.3 空指针访问成员函数
C++中的空指针也是可以调用成员函数的,但是也要注意有没有用到this
指针
如果用到this指针,需要加以判断保证代码的健壮性
#include<iostream>
using namespace std;
class Person
{
public:
void ShowClassName()
{
cout << "我是Person类" << endl;
}
void ShowPerson()
{
if (this == NULL)
{
return;
}
cout << "age=" << this -> m_Age << endl;
}
int m_Age;
};
void test01()
{
Person* p = NULL;
p->ShowClassName();
p->ShowPerson();
}
int main()
{
test01();
return 0;
}
4.3.4 const修饰成员函数
常函数:
成员函数后加const后我们称这个函数为常函数
常函数内不可以修改成员属性
成员属性声明时加关键字mutable后,在常函数中依然可以修改
常对象:
声明对象前加const称该对象为常函数
常对象只能调用常函数
mutable 在加入这个修饰符后,成员函数便可以修改
5 友元
一个在类外定义的函数,如何访问私有成员呢
方法就是在定义的时候,声明该函数是友元
#include <iostream>
using namespace std;
class Buliding
{
friend void goodGay(Buliding* buliding);
public:
string m_SittingRoom;
void Building()
{
this->m_SittingRoom = "客厅";
this->m_BedRoom = "卧室";
}
private:
string m_BedRoom;
};
void goodGay(Buliding *buliding)
{
cout << "好基友正在访问:" << buliding->m_SittingRoom << endl;
cout << "好基友正在访问:" << buliding->m_BedRoom << endl;
}
int main()
{
Buliding b;
goodGay(&b);
}
6运算符重载
6.1 四则运算运算符
由于C++中,同一类不同对象的运算符不能直接进行加减乘除运算,所以需要使用到重载运算符,具体的使用方法见下面的代码
#include <iostream>
using namespace std;
class Person
{
public:
int m_A;
int m_B;
//成员函数运算符
//Person operator+(Person& p)
//{
// Person temp;
// temp.m_B = this->m_B + p.m_B;
// temp.m_A = this->m_A + p.m_A;
// return temp;
//}
};
//全局运算符
Person operator+(Person& p1, Person& p2)
{
Person temp;
temp.m_A = p1.m_A + p2.m_A;
temp.m_B = p1.m_B + p2.m_B;
return temp;
}
void test01()
{
Person p1;
Person p2;
p1.m_A = 10;
p1.m_B = 10;
p2.m_A = 10;
p2.m_B = 10;
Person p3 = p1 + p2;
//可以简写p3=p1+p2的形式,也可以用本质的形式来表示
//全局重载运算符的表达形式 p3=operator(p1,p2)
//成员函数运算符的表达形式 p3=p1.operator+(p2)
cout << "p3.m_A=" << p3.m_A << endl;
cout << "p3.m_B=" << p3.m_B << endl;
}
int main()
{
test01();
return 0;
}
6.2 左移运算符 <<
7 继承
7.1 继承的基本语法
#include <iostream>
using namespace std;
class Bease
{
public:
void hread()
{
cout << "头" << endl;
}
void teil()
{
cout << "尾" << endl;
}
};
class Java : public Bease
{
public:
void content()
{
cout << "Java" << endl;
}
};
class Python : public Bease
{
public:
void content()
{
cout << "Python" << endl;
}
};
class Cpp : public Bease
{
public:
void content()
{
cout << "Cpp" << endl;
}
};
void text01()
{
Python py;
py.hread();
py.teil();
py.content();
Java Ja;
Ja.hread();
Ja.teil();
Ja.content();
Cpp cp;
cp.hread();
cp.teil();
cp.content();
}
int main()
{
text01();
return 0;
}
主要就是这么一句话就可以实现继承(class 子类名称 : public 父类名称)
7.2 继承的三种类型
7.3继承的规则
在继承中,子类是把父类的所有成员都进行继承了,但是私有成员是访问不到的,因为编译器把私有成员给隐藏了。
7.4继承出现同名的情况
当子类和父类中出现同名的成员时,优先调用子类的成员,如果想调用父类成员,需要加作用域
#include <iostream>
using namespace std;
class Father
{
public:
int m_A = 10;
};
class Son : public Father
{
public:
int m_A = 20;
};
void test01()
{
Son S;
//cout << S.m_A << endl;
cout << S.Father::m_A << endl;
}
int main()
{
test01();
return 0;
}
7.5 菱形继承
#include <iostream>
using namespace std;
class Animal
{
public:
int m_A = 10;
};
class Sheep :virtual public Animal
{
};
class Tuo :virtual public Animal
{
};
class Caonima : public Sheep, public Tuo
{
};
void test01()
{
Caonima C;
//cout << S.Tuo::m_A << endl;
cout << C.Tuo::m_A << endl;
}
int main()
{
test01();
return 0;
}
8 多态
多态的基本语法
8.1 什么是多态
多态是同一个行为具有多个不同表现形式或形态的能力。
多态就是同一个接口,使用不同的实例而执行不同操作,如图所示:
#include<iostream>
using namespace std;
class Animal
{
public:
virtual void speak()
{
cout << "这是一只动物" << endl;
}
};
class cat : public Animal
{
public:
void speak()
{
cout << "这是一猫" << endl;
}
};
class dog : public Animal
{
public:
void speak()
{
cout << "这是一只狗" << endl;
}
};
void DoSpeak(Animal & animal)
{
animal.speak();
}
void test01()
{
cat C;
DoSpeak(C);
dog D;
DoSpeak(D);
}
int main()
{
test01();
return 0;
}
7.2多态的使用案例
下面这个案例,将使用原本的方式和多态的方式分别来实现计算器功能
1.不用多态的方式实现一个计算器
class Calculator {
public:
int getResult(string oper)
{
if (oper == "+") {
return m_Num1 + m_Num2;
}
else if (oper == "-") {
return m_Num1 - m_Num2;
}
else if (oper == "*") {
return m_Num1 * m_Num2;
}
//如果要提供新的运算,需要修改源码
}
public:
int m_Num1;
int m_Num2;
};
void test01()
{
//普通实现测试
Calculator c;
c.m_Num1 = 10;
c.m_Num2 = 10;
cout << c.m_Num1 << " + " << c.m_Num2 << " = " << c.getResult("+") << endl;
cout << c.m_Num1 << " - " << c.m_Num2 << " = " << c.getResult("-") << endl;
cout << c.m_Num1 << " * " << c.m_Num2 << " = " << c.getResult("*") << endl;
}
2.用多态的方式实现一个计算器
#include<iostream>
using namespace std;
class Calculator
{
public:
int Num_1;
int Num_2;
};
class AddCalculator : public Calculator
{
public:
int getResult()
{
return Num_1 + Num_2;
}
};
class SubClauclator : public Calculator
{
public:
int getResult()
{
return Num_1 - Num_2;
}
};
void test01()
{
//Calculator* abc = new AddCalculator;
//abc->Num_1 = 10;
//abc->Num_2 = 20;
AddCalculator Add;
Add.Num_1 = 10;
Add.Num_2 = 20;
cout << Add.Num_1 << "+" << Add.Num_2 << "=" << Add.getResult() << endl;
}
int main()
{
test01();
return 0;
}
将上面的这两段代码进行比较,我们会发现,明明用函数的方式就可以实现计算器的功能,为什么还需要再次学多态的方式呢?
在编写简单的代码时,只需要一个人就可以完成工作时,用函数的方式来写代码,效率会更高,编写的代码会少很多。
但是如果编写大量的代码呢,很多时候时一个数十人的团队来编写代码,再用函数的方式进行编写效率反而会下降,同时不利于代码的扩展和维护(因为需要修改源码),而使用多态就可以避免这种现象,同时还可以增加代码的可读性。所以在学习,做一些实验项目时,使用函数的方式会比较轻松。在以后的工作中,尤其是参与中大型的项目时,使用多态的方式编写代码是必须的。
7.3纯虚函数和抽象类
在多态中,父类中的虚函数的没有任何意义的,主要目的是调用子类中的内容,因此可以将虚函数改写为纯虚函数
纯虚函数语法:virtual 返回值类型 函数名 (参数列表)=0;
抽象类特点:
子类无法实例化对象
子类必须重写抽象类中的纯虚函数,否则也属于抽象类
纯虚函数的创建语句
virtual void func()=0;//这么设计就是逼迫开发者在派生类中重写函数
案例
#include<iostream>
using namespace std;
class Father
{
public:
virtual void func() = 0;
};
class Son :public Father
{
void func()
{
cout << "纯虚函数子类" << endl;
}
};
void test01()
{
Father* F = new Son;
F->func();
delete F;
}
int main()
{
test01();
return 0;
}
案例二
#include<iostream>
using namespace std;
class Water
{
public:
virtual void Boil() = 0;
virtual void Brew() = 0;
virtual void PourInCup() = 0;
virtual void PutSomething() = 0;
void makeDrink()
{
Boil();
Brew();
PourInCup();
PutSomething();
}
};
class Coffe : public Water
{
public:
virtual void Boil()
{
cout << "煮矿泉水" << endl;
}
virtual void Brew()
{
cout << "冲泡咖啡" << endl;
}
virtual void PourInCup()
{
cout << "倒入纸杯中" << endl;
}
virtual void PutSomething()
{
cout << "加入糖和牛奶" << endl;
}
};
void doWork(Water* abs)
{
abs->makeDrink();
delete abs;
}
int main()
{
doWork(new Coffe);
return 0;
}
前面说了一大堆,那么纯虚函数有什么用处呢,主要目的是让子类必须创建父类中创建的纯虚函数,否则子类无法创建,这个是重点!!
7.4 虚析构和纯虚析构
在使用父类指针,调用子类函数时,父类函数会进行释放,但是子类函数没有进行释放,这回造成堆区的内存泄露。(内存泄漏会占用内存空间,进而导致程序运行速度减慢,甚至是崩溃)
目前水平有限,对这下面段代码一知半解,仅仅是跟着敲了一遍
#include<iostream>
using namespace std;
class Animal
{
public:
Animal()
{
cout << "Animal构造函数调用" << endl;
}
virtual ~Animal()
{
cout << "Animal析构函数调用" << endl;
}
virtual void Speak() = 0;
};
class Cat : public Animal
{
public:
Cat(string name)
{
cout << "Cat构造函数调用!" << endl;
m_Name = new string(name);
}
~Cat()
{
cout << "Cat析构函数调用!" << endl;
if (this->m_Name != NULL)
{
delete m_Name;
}
}
virtual void Speak()
{
cout << *m_Name << "小猫在说话!" << endl;
}
string * m_Name;
};
void test01()
{
Animal* animal = new Cat("Tom");
animal->Speak();
delete animal;
}
int main()
{
test01();
return 0;
}
8.文件操作
对文件进行操作时需要的语句
打开方式 | 解释 |
---|---|
ios::in | 为读文件而打开文件 |
ios::out | 为写文件而打开文件 |
ios::ate | 初始位置:文件尾 |
ios::app | 追加方式写文件 |
ios::trunc | 如果文件存在先删除,再创建 |
ios::binary | 二进制方式 |
注意: 文件打开方式可以配合使用,利用|操作符
**例如:**用二进制方式写文件 ios::binary | ios:: out
8.1 写文件
写文件步骤如下:
流程 | 语句 |
---|---|
1包含头文件 | #include < fstream> |
2 创建流对象 | ofstream ofs; |
3打开文件 | ofs.open(“文件路径”,打开方式); |
4写数据 | ofs << “写入的数据”; |
5关闭文件 | ofs.close(); |
#include<fstream>
#include<string>
#include<iostream>
using namespace std;
//void test01()
//{
// ifstream ifs;
// ifs.open("test.txt,ios::in");
// if (!ifs.is_open())
// {
// cout << "文件打卡失败" << endl;
// return;
// }
//
//}
void test01()
{
ofstream ofs;
ofs.open("test.txt", ios::out);
ofs << "姓名:张三" << endl;
ofs << "姓名:男" << endl;
ofs << "年龄:18" << endl;
ofs.close();
}
int main()
{
test01();
return 0;
}
8.2 读文件
读文件的流程 | 代码 |
---|---|
1.包含头文件 | #include |
2.创建流对象 | ifstream ifs |
打开文件并判断文件是否打开成功 | ifs.open(“文件路径”,打开方式) |
读数据 | 四种方式 |
关闭文件 | ifs.close() |
读文件代码如下:
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
void test01()
{
ifstream ifs;
ifs.open("test.txt", ios::in);
if (!ifs.is_open())
{
cout << "文件打开失败" << endl;
return;
}
//第一种方式
//char buf[1024] = { 0 };
//while (ifs >> buf)
//{
// cout << buf << endl;
//}
//第二种
//char buf[1024] = { 0 };
//while (ifs.getline(buf,sizeof(buf)))
//{
// cout << buf << endl;
//}
//第三种
//string buf;
//while (getline(ifs, buf))
//{
// cout << buf << endl;
//}
char c;
while ((c = ifs.get()) != EOF)
{
cout << c;
}
ifs.close();
}
int main()
{
test01();
return 0;
}