C++学习笔记

本笔记为文本在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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值