C++知识点

一.C++内存四区:

面向对象的三大特征:封装、继承、多态
封装:利用抽象数据类型将数据和基于数据的操作封装在一起,只保留一些对外接口。
继承:子类继承父类的所有内容,增加程序的可扩展性
多态:通过一个父类对象能够访问多个子类接口,动态绑定

1.代码区:存放二进制代码,操作系统进行管理,成员函数和非成员函数
2.全局区:存放全局变量、静态变量和常量
3.栈区:存放函数的参数值、局部变量,由编译器进行管理,声明的对象如果没有用new,则也是存放在栈中的
4.堆区:由程序员进行内存管理,否则程序结束时由操作系统回收

二.指针

1.指针必须初始化
2.指针变量和普通变量的区别

  • 普通变量存放的是数据,指针变量存放的是地址
  • 指针变量可以通过" * "操作符,操作指针变量指向的内存空间,这个过程称为解引用

3.当两个指针同时指向一块内存时,要释放这块内存的话,合法的操作应该是:先将一个指针置为NULL,再delete另一个指针(因为delete,就是告诉编译器这块内存已经没有被占用了,所以只能delete一次)

4.对基本数据类型,delete和delete[] 的结果是完全一样的,对于对象数组来说,必须用delete[] .单个对象用delete

三.模板的友元类

template <class Type> friend class 类名;

四.C语言的字符和字符串

在C语言中实际上是没有字符串的,只有char类型。
当用字符数组表示字符串时,会默认在末尾加上‘/0’,因此实际的字符数组空间要加一。

char str[100] = "hello world!";
char *p = (char *) malloc(sizeof(char) * n);

五、struct和class的区别

在C++中 struct和class唯一的区别就在于 默认的访问权限不同(其他没有什么太大区别)

区别:

  • struct 默认权限为公共
  • class 默认权限为私有

六、深拷贝和浅拷贝

浅拷贝:简单的赋值拷贝操作

深拷贝:在堆区重新申请空间,进行拷贝操作

对于基本数据类型,两个没有区别。但如果类中存在new出来的内存,也就是在堆内的内存,那么浅拷贝就相当于取别名,会导致释放时在堆内重复释放,而深拷贝就是在堆内申请内存,再将值复制到内存中。

#include<iostream>
#include<vector>
using namespace std;

class Animal {
public:
	int* p = new int[10];
	Animal(int a[10]){
		for (int i = 0; i < 10; i++) {
			p[i] = a[i];
		}
	}
	virtual void doSpeak(){
		cout << "动物在说话" << endl;
	}
	void change() {
		p[0] = 100;
	}
};
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	Animal animal(arr);
	cout << animal.p[0] << endl;
	Animal an2 = animal;
	cout << an2.p[0] << endl;

	animal.change();
	cout << animal.p[0] << endl;
	cout << an2.p[0] << endl;

	return 0;
}

在这里插入图片描述
如上图,浅拷贝下,改变了animal中的值,an2里面的值也被改变了,证明两个指向了同一块内存。

#include<iostream>
#include<vector>
using namespace std;

class Animal {
public:
	int* p ;
	Animal(int a[10]){
		p = new int[10];
		for (int i = 0; i < 10; i++) {
			p[i] = a[i];
		}
	}
	Animal(const Animal& animal) {
		p = new int[10];
		for (int i = 0; i < 10; i++) {
			p[i] = animal.p[i];
		}
	}
	virtual void doSpeak(){
		cout << "动物在说话" << endl;
	}
	void change() {
		p[0] = 100;
	}
};
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	Animal animal(arr);
	cout << animal.p[0] << endl;
	Animal an2 = animal;
	cout << an2.p[0] << endl;

	animal.change();
	cout << animal.p[0] << endl;
	cout << an2.p[0] << endl;

	return 0;
}

在这里插入图片描述
如上图,重写拷贝函数进行深拷贝就没有这个问题了。

七、类中内存问题

类所占内存的大小是由成员变量(静态变量除外)决定的,虚函数指针和虚基类指针也属于数据部分,成员函数是不计算在内的。因为在编译器处理后,成员变量和成员函数是分离的。成员函数还是以一般的函数一样的存在。a.fun()是通过fun(a.this)来调用的。所谓成员函数只是在名义上是类里的。
成员函数和非成员函数是放在代码区的
其实成员函数的大小不在类的对象里面,同一个类的多个对象共享函数代码。
无论有多少个虚函数,都只有一个虚函数表指针(vfptr)
而我们访问类的成员函数是通过类里面的一个指针实现,而这个指针指向的是一个table,table里面记录的各个成员函数的地址(当然不同的编译可能略有不同的实现)。所以我们访问成员函数是间接获得地址的。所以这样也就增加了一定的时间开销,这也就是为什么我们提倡把一些简短的,调用频率高的函数声明为inline形式(内联函数
为什么空的什么都没有是1呢?
c++要求每个实例在内存中都有独一无二的地址。空类也会被实例化,所以编译器会给空类隐含的添加一个字节,这样空类实例化之后就有了独一无二的地址了。所以空类的sizeof为1

八、静态成员

1、静态成员变量:所有对象共享同一份数据,类内声明,类外初始化
**2、静态成员函数:**所有对象共享同一个函数,静态成员函数只能访问静态成员变量

3、静态成员的访问方式:
(1)通过对象访问object.a或者object.fun(),
(2)通过类名访问:类名::a 或者 类名::fun().

4、 成员变量和成员函数分开存储
(1)非静态成员变量占空间
(2)静态变量不占空间
(3)静态成员函数不占对象空间
(4)非静态成员函数不占空间,所有函数共享一个函数实例

5、每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码

那么问题是:这一块代码是如何区分那个对象调用自己的呢?
this指针指向被调用的成员函数所属的对象

九、const的使用

1、成员函数用const修饰时:const func(); 这相当于修饰指针this: cnst this
则const修饰指针时,指针指向的对象不能改变,即不能改变成员变量的值。
2、常量指针 :Animal const p : p指向的值不能改变,即p
指针常量 :Animal * const p : p指向的地址不能改变

十、构造和析构的顺序

构造时:父类先构造,子类后构造
析构时:子类先析构,父类后析构

当子类和父类中出现同名函数,子类会隐藏父类的函数,如果要通过子类访问父类的函数,需要加作用域 son.father::func();

十一、函数重载

1、左移运算符重载

#pragma once
#include<iostream>
using namespace std;

//1.重载有两种方式:成员函数和全局函数
//左移运算符重载:用全局函数

class Person {
public:
	int a;
	int b;
	//void operator<<(Person& p) {  //成员函数重载:这么重载后的调用是 p1.operator<<(p2)  这个形式不对
	//}
};
ostream& operator<<(ostream& cout, Person& p) { //全局函数重载:operator<<(cout,p),简写为cout<<p;
	cout << p.a << " " << p.b;             //注意:cout只能有一个,所以用引用的方式
	return cout;
}
int main() {
	Person p;
	p.a = 1;
	p.b = 2;
	cout << p <<endl;  //但是如果要实现cout<<p1<<p2<<...,必须将重载函数的返回置为cout的类型ostream,将返回值再次进行调用
}

十二、文件操作

在这里插入图片描述
Person p = {“张三”,24}; //自己创建的对象还可以这么赋值

#include<iostream>
#include<vector>
#include <string>    //为了使用getline函数
#include <fstream>    //头文件
using namespace std;

//读文件用ifstream  
//写文件ofstream
//读写文件用fstream

class Person {
public:
	char name[64];   //这里使用c++的string会出问题
	int age;
};

int main()
{
	//ofstream ofs;
	//ofs.open("123.txt",ios::out);    //路径地址限制在23个字符内
	也可以 ofstream ofs("123.txt",ios::out | ios::binary);	
	//if (!ofs.is_open()) {
	//	cout << "文件打开失败!" << endl;
	//}
	//ofs << "hello world" << endl;     //写出到文件中
	//ofs << "我是你爸爸" << endl;
	//ofs.close();              //关闭文件流

	//ifstream ifs;
	//ifs.open("123.txt",ios::in);
	//if (!ifs.is_open()) {
	//	cout << "文件打开失败!" << endl;
	//}

	//读取方式1
	//string buf;
	//while (getline(ifs, buf)) {    //getline是头文件string中的方法
	//	cout << buf << endl;
	//}

	//读取方式2
	//char arr[1024];
	//while (ifs.getline(arr,sizeof(arr))) {   //整行读取
	//	cout << arr << endl;
	//}

	//读取方式3
	//char ar[1024];
	//while (ifs >> ar) {    //这种方式是以空格为间断符
	//	cout << ar << endl;
	//}
	//ifs.close();


	//二进制方式读写文件
	ofstream ofs1("124.txt",ios::binary | ios::out);  //如果没有该文件,会直接创建一个文件
	if (!ofs1.is_open()) {
		cout << "文件打开失败" << endl;
	}
	Person p = {"张三",24};    //自己创建的对象还可以这么赋值
	ofs1.write((const char*)&p, sizeof(p));   //二进制写入的数据会乱码,但读取出来不会出问题
	ofs1.close();

	ifstream ifs1("124.txt",ios::binary | ios :: in);
	if (!ifs1.is_open()) {
		cout << "文件打开失败" << endl;
	}
	Person p1;
	ifs1.read((char *)&p1,sizeof(p1));  //存放数据的地址
	cout << p1.name << " " << p1.age << endl;
	ifs1.close();

	return 0;
}

十三、C++之STL

1、STL包含六个组件:容器、算法、迭代器、仿函数、适配器、空间配置器

容器:各种数据结构,如vector、stack等
算法:各种算法实现,如find、sort等
**迭代器:算法和容器之间的桥梁,提供一种方法,使之能够访问某个容器中的所有数据,**而不会暴露容器的内部表示方式
仿函数:重载了运算时调用的运算符,,仿函数是一个类,在类中进行operator小括号的重载

2、
vector容器是单端动态数组:不是链表,内存是连续的,在头部插入时,后面所有数据都要移动

#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;

void myPrint(int x) {
	cout << x << endl;
}

int main()
{
	vector<int> a(10);  //10代表存放数据的个数,通过a.resize()可以改变
	//存放数据1
	for (int i = 0; i < 10; i++) {
		a[i] = i;
	}
	//存放数据2
	a.push_back(12); //存放数据,每次都会重新分配内存

	//a.resize(15);   //重新分配内存大小
	cout << a.front() << endl;
	cout << a.back() << endl;
	a.erase(a.begin()+3);  //删除某个元素或某段元素  ,除了begin、end还有rbegin、rend
	cout << a.empty() << endl;

	遍历方式1
	//for_each(a.begin(),a.end(),myPrint);   //for_each方法是头文件algorithm中的,调用myPrint函数	
	遍历方式2
	//for (vector<int>::iterator it = a.begin(); it != a.end(); it++) {
	//	cout << *it << endl;
	//}
	遍历方式3
	//for (int i = 0; i < a.size(); i++) {
	//	cout << a[i] << endl;
	//}
	遍历方式4
	for (auto i : a) {
		cout << i << endl;
	}

	return 0;
}

deque是双端数组:头部的插入删除也快,类似双向链表,内存不连续

#include<iostream>
#include<algorithm>
#include <set>
#include<list>
#include <deque>
#include <vector>
#include <queue>

using namespace std;

//大部分容器操作相似,只有赋值和取值可能有差异
//删除:erase(begin,(end))  ,查找: find(key);  返回迭代器的位置
//swap()交换两个set的数据


int main()
{	
	
	list<int> l;    //list只能通过push_back()赋值
	l.push_back(56);
	l.push_back(23);
	l.push_back(12);
	for (auto it : l) {
		cout << it << " ";
	}
	cout << "list" << endl;


	queue<int> q;  //队列,没有迭代器?
	q.push(12);
	q.push(5);
	//q.swap();  swap交换两个队列的数据


	vector<int> v(10); // 只有声明时确定了大小,才能这种方式赋值,后面通过resize()重置大小


	deque<int> d(10);
	for (int i = 0; i < 5; i++) {  //只有声明确定了大小,才能这种方式赋值
		d[i] = i;
	}
	d.push_front(18);   //与vector的使用区别,vector没有这个方法,因此deque叫双端队列
	for (auto it : d) {
		cout << it << " ";
	}
	cout << "deque" << endl;


	//pair: 对组,不需要头文件
	pair<char, int> p('a', 1);
	pair<char, int> p1 = make_pair('b',2);  //两种创建方式
	cout << p.first << p.second <<"pair" <<endl;
	cout << p1.first << p1.second <<"pair" <<endl;

	return 0;
}

queue:队列,先进先出

list: 双向循环链表

前四个类型用法相似

set /multiset:所有元素在插入时自动排序,底层用二叉树实现。set不允许有重复元素出现,multiset允许。注意当数据类型是自定义时,自动排序可能出现问题,通过仿函数解决

class Person {
public:
	char name[64];
	int age;
};
class comparePerson {
public:
	bool operator()(const Person& p1, const Person& p2) const{
		return p1.age > p2.age;  //按年龄从大到小排序
	}
};
int main(){
set<int> s;   //set只能用insert赋值,set不记录重复值,multiset支持,都是自动排序,自定义类型需要自己指定排序规则
s.insert(56);    //只能通过insert()插入
s.insert(56);   
s.insert(2);
s.insert(8);
multiset<int> ms;
ms.insert(56);
ms.insert(56);
ms.insert(2);
ms.insert(8);
cout << "set size:" << s.size() << endl;
for (set<int>::iterator it = s.begin(); it != s.end(); it++) {
	cout << *it << " ";
}
cout << "set" << endl;
for (multiset<int>::iterator it = ms.begin(); it != ms.end(); it++) {
	cout << *it << " ";
}
cout << "multiset" << endl;

Person person1 = {"张三",23};    //当自定义数据类型时,编译器不知道该以哪个为标准进行排序,所以报错
Person person2 = { "李四",22 };  //通过仿函数解决这个问题
Person person3 = { "王五",26 };
Person person4 = { "张龙",15 };
set<Person,comparePerson> sp;   //自定义数据类型通过仿函数解决自动排序问题comparePerson类
sp.insert(person1);
sp.insert(person2);
sp.insert(person3);
sp.insert(person4);
for (set<Person, comparePerson>::iterator it = sp.begin(); it != sp.end(); it++) {
	cout << (*it).name << " " << (*it).age << "set自定义排序" << endl;
}
return 0;
}

pair : 对组

map/multimap : 键值对,有key和value两个值,元素按照key自动排序,底层用二叉树。 优点是通过key可以直接映射到对应的value。
map不允许出现重复key,multimap允许出现.

#include<iostream>
#include<algorithm>
#include <map>
#include<queue>

using namespace std;

class Person {
public:
	string name;
	int age;
};
class compareMap {
public:  //这里加&是防止拷贝构造一个新的副本,减少内存消耗
	bool operator() (const Person& p1, const Person& p2) const{
		return p1.age < p2.age;
	}
};

int main()
{	
	map<char, int> m;     //map里面存放的是pair类型的数据  pair<key,value>(k,v);
	m.insert(pair<char,int>('a',1));  //pair<char,int>()的匿名对象传递	
	m.insert(pair<char, int>('c', 3));
	m.insert(pair<char, int>('b', 2));

	for (map<char, int>::iterator it = m.begin(); it != m.end(); it++) {
		cout << (*it).first <<" " <<it->second<< endl;  //两种表现形式:(*it).first    it->second
	}

	map<char, int> m2(m);  //拷贝构造

	//两个map容器的交换   swap()函数

	//删除元素:erase()  1.根据迭代器进行删除m.erase(begin,end),end可省略  2.根据键key进行元素删除

	//查找  : find()  返回迭代器位置   count 返回key的元素个数

	
	//改变排序算法
	Person p1 = {"张三",56};
	Person p2 = { "李四",12 };
	Person p3 = { "王五",43 };
	map<Person, int,compareMap> mp;
	mp.insert(pair<Person,int>(p1,1));
	mp.insert(pair<Person, int>(p2, 1));
	mp.insert(pair<Person, int>(p3, 1));

	for (map<Person, int, compareMap>::iterator it = mp.begin(); it != mp.end(); it++) {
		cout << (*it).first.name<<" " <<(*it).first.age << " " << it->second << endl;  //两种表现形式:(*it).first    it->second
	}

	return 0;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值