一、C++结构体相关知识
一段代码阐释所有初始化与赋值的情况:
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
//结构体初始化的几种方式
//定义一个node结构体
typedef struct node{
int id;
int num;
string name;
//1、无参数的构造函数数组初始化时调用
//node():id(),num(),name(){};
//有参构造
//node(int a,int b,string c):id(a),num(b),name(c){}
//2、对于方法3的构造函数初始化,什么都不写就是使用的结构体自带的默认构造函数。
//如果自己重写了带参数的构造函数,就不能用大括号进行初始化了,即不能再使用指定初始化与顺序初始化了(初始化结构体时如果不传入参数会出现错误)。
//在建立结构体数组时,如果只写了带参数的构造函数将会出现数组无法初始化的错误
//3、放在最后的构造函数可以不加分号结尾
//node(int id,int num,string name){
// this->id=id;
// this->num=num;
// this->name=name;
// }
} node_t;
typedef struct charnode{
int id;
int num;
char name[16];
} charnode_t;
struct Test {
int x = 0; // <== 含有默认初始化值的元素,无法使用初始化列表进行初始化
char v;
};
class Hero {
public:
Hero() {
cout << "Hero 构造函数调用了" << endl;
}
//如果用下面的free释放new出来的h2内存,将不会触发下面的析构函数
//Hero *h2 = new Hero;
//free(h2);
~Hero() {
cout << "Hero 析构函数调用了" << endl;
}
};
struct Date{
int day, month, year;
};
int main(){
//初始化的几种方法
//1、顺序初始化
node node1st={1,2,"node1st"};
// node node1st;
cout<<node1st.name<<"---"<<"size:"<<sizeof(node1st)<<endl;
//2、指定初始化(点号+赋值符号或者使用冒号)
struct node node2st={.id=2,num:5,name:"node2st"};
cout<<node2st.name<<endl;
//3、构造函数初始化
//构造函数初始化常见于 C++ 代码中,因为 C++ 中的 struct 可以看作 class,结构体也可以拥有构造函数
//struct 如果定义了构造函数的话,就不能用大括号进行初始化了,即不能再使用指定初始化与顺序初始化了
node_t node3st(5,6,"node3st");
cout<<node3st.name<<endl;
//4、使用calloc分配并置为0
node_t * node4st=(node_t *)calloc(1,sizeof(struct node));
std::cout << node4st->name<<"--point size:"<<sizeof(node4st)<<endl;
//5、使用new动态创建结构体变量时,必须是结构体指针类型
node_t * node5st=new node();
//虽说delete可以释放calloc的内存,但是我编译器还是报错了,还是得用free来释放
delete node5st;
// free(node5st);
//6、结构体变量使用结构体+()可以初始一个结构体变量的(也就创建了一个新的引用)
node node6st;
node6st=node();
cout<<node6st.name<<"--node6st--"<<"size:"<<sizeof(node6st)<<endl;
//7、以前是可以部分初始化的,但部分初始化只限于初始化开头的变量,如果某个结构成员未被初始化,则所有跟在它后面的成员都需要保留为未初始化
//不过我的编辑器部分初始化也报错了,感觉可能部分初始化风险较大吧
// Date birthday = {23,8};
//结构体赋值的几种方法
//变量的赋值和初始化是不一样的,初始化是在变量定义的时候完成的,是属于变量定义的一部分,赋值是在变量定义完成之后想改变变量值的时候所采取的操作
//无法使用node1st={1,2,"node1st"};这种方式赋值
//1、使用 memset 对结构体变量进行置空操作
//如下,使用memset进行赋值时,结构体里不能是string类型,但可以是char或char*
charnode_t node1ass;
// 按照编译器默认的方式进行初始化(如果node4st是全局静态存储区的变量,默认初始化为0,如果是栈上的局部变量,默认初始化为随机值)
memset(&node1ass,0,sizeof(charnode_t));
cout<<node1ass.name<<endl;
//2、依次给每一个结构体成员变量进行赋值
node_t node2ass;
node2ass.id=5;
node2ass.name="node2ass";
cout<<node2ass.name<<endl;
//3、使用已有的结构体变量给另一个结构体变量赋值(结构体变量之间是可以相互赋值的)
node_t node3ass;
node3ass=node1st;
cout<<node3ass.name<<endl;
//ps:初始化与赋值有着本质的区别,初始化是变量定义时的第一次赋值,赋值则是定义之后的值的变更操作
node3ass.name="node3ass";
cout<<node3ass.name<<"--"<<node1st.name<<endl;
//4、使用指针指向一个结构体变量
node_t *node4ass;
// node4ass=&node1st;
node4ass=node4st;
cout<<node4ass->name<<endl;
node4ass->name="node4ass";
// cout<<node4ass->name<<"--"<<node1st.name<<endl;
cout<<node4ass->name<<"--"<<node4st->name<<endl;
//1、delete主要用来释放new分配的内存,但delete 也可以用来释放由 malloc 和 calloc 分配的内存
// delete node4ass;
// delete node4st;
//2、free主要用来释放由 malloc 和 calloc 分配的内存,也可以释放new 分配的内存,但不推荐使用
free(node4ass);
// free(node4st);
// cout<<node4ass->name<<"--"<<node4st->name<<endl;
//char *c1 = (char *)malloc(1, 10);
//c1[0] = 'a';
//delete[] c1;
//经过 delete 释放过后,指针变成了野指针,此时再访问内存将是非法的,没有任何数据的,所以会显示乱码
//free(c1);
//不要使用 free 去释放由 new 分配的内存,因为它 不会去触发析构函数
return 0;
}
运行结果
node1st---size:40
node2st
node3st
--point size:8
--node6st--size:40
node2ass
node1st
node3ass--node1st
node4ass--node4ass
二、容器操作备忘
- vector容器的操作
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<vector>
#include<list>
#include<algorithm>
using namespace std;
void myprint(int val){
cout<<val<<endl;
}
int main(){
//容器初始化的几种方法:
//1、默认初始化,都是空
std::vector<int> vec1;
// cout<<vec1[0]<<endl;
//2、列表初始化
std::vector<int> vec2={1,2,3,5};
cout<<vec2[0]<<endl;
//3、拷贝初始化
std::vector<int> vec3=vec2;
//4、填充初始化
std::vector<int> vec4(5, 10); // 创建一个包含5个值为10的元素的vector
//初始化一个尺寸为6的容器数组
std::vector<int> vec44(6);
cout<<vec44[0]<<"-vec44 size:"<<vec44.size()<<endl;
//5、使用迭代器进行初始化
std::vector<int> vec5 = {8, 2, 3, 4, 5};
//不是最高效的方法,因为它涉及到元素的复制
std::vector<int> vec5s(vec5.begin()+1, vec5.end());
cout<<"vec5s[0]:"<<vec5s[0]<<endl;
std::list<int> lst1(vec5.begin(), vec5.end());
//6、使用emplace方法进行初始化
std::vector<std::string> vec6;
vec6.emplace_back("Hello, World!");
cout<<vec6[0]<<endl;
std::vector<std::pair<int, std::string>> vecpair;
vecpair.emplace_back(1, "one"); // 使用emplace方法初始化
cout<<"vecpair[0].second:"<<vecpair[0].second<<endl;
//7、使用std::move进行初始化
//C++11引入了右值引用和std::move,可以将对象的资源从一个对象移动到另一个对象,而不是复制它们。
//这对于那些拥有大量资源的对象(如std::vector或std::string)来说是非常有用的
std::vector<int> vec7 = std::move(vec5);
cout<<"vec7:"<<vec7[0]<<endl;
//移动后vec5数组再也无法使用
// cout<<"vec5:"<<vec5[0]<<endl;
//容器的相关操作:
//rbegin() 返回Vector尾部的逆迭代器
//1、末尾添加新元素
vec2.push_back(8);
//2、判空,非空为0
cout<<"empty:"<<vec2.empty()<<endl;
//3、输出尺寸
cout<<"size:"<<vec2.size()<<endl;
//4、遍历
for(vector<int>::iterator it=vec2.begin();it!=vec2.end();it++){
cout<<*it<<endl;
}
for(auto val: vec2)
cout<<"auto:"<<val<<endl;
//5、指定位置插入
vec2.insert(vec2.begin()+2,88);
//6、删除最后一个元素
vec2.pop_back();
//7、删除指定位置的元素
vec2.erase(vec2.begin());
//全删
//vec2.erase(vec2.begin(),vec2.end());
//再次遍历,需要包含algorithm的头文件
for_each(vec2.begin(), vec2.end(), [](int val)->void { cout << val << endl;});
//8、返回特定元素:
cout<<"vec2[1]:"<<vec2[1]<<endl;
cout<<"vec2.at(1):"<<vec2.at(1)<<endl;
cout<<"vec2.front():"<<vec2.front()<<endl;
cout<<"vec2.back():"<<vec2.back()<<endl;
//9、assign(beg, end); //将[beg, end)区间中的数据拷贝赋值给本身
vector<int>vec8;
vec8.assign(5,10);
//10、清空
vec2.clear();
cout<<"size:"<<vec2.size()<<"capacity:"<<vec2.capacity()<<endl;
//11、重新配置容器长度
vec2.resize(5);
cout<<"size:"<<vec2.size()<<"capacity:"<<vec2.capacity()<<endl;
//12、互换容器
vec2.swap(vec7);
for(auto val: vec2)
cout<<"vec2:"<<val<<endl;
//13、拷贝容器
vec5.resize(5);
copy(vec2.begin()+2,vec2.end(),vec5.begin());
for(auto val: vec5)
cout<<"vec5:"<<val<<endl;
//14、排序-正向排序
sort(vec5.begin(), vec5.end());
for(auto val: vec5)
cout<<"sort vec5:"<<val<<endl;
//反向排序
// sort(vec5.rbegin(), vec5.rend(), greater<int>());
sort(vec5.rbegin(), vec5.rend(), less<int>());
for(auto val: vec5)
cout<<"anti sort vec5:"<<val<<endl;
//15、反转容器内容
reverse(vec5.begin(),vec5.end());
cout<<"afer reverse:"<<endl;
for_each(vec5.begin(),vec5.end(),myprint);
//16、查找指定元素
//vector<int>::iterator it = find(vec5.begin(), vec5.end(), 3);//找到3的位置
//vec5.insert(it, 0);//在原3的位置插入,也就是插入到了3的前面
//栈
//stack<int> xstack;
//1、入栈
//xstack.push(10);
//2、返回栈顶元素
//xstack.top();
//3、栈顶(也就是最后进入的)元素出栈
//xstack.pop();
//队列
//queue<int>xque;
//1、入队
//xque.push(4);
//2、队头元素
//xque.front();
//3、队尾元素
//xque.back();
//4、队头出队
//xque.pop();
reurn 0;
}
输出:
1
0-vec44 size:6
vec5s[0]:2
Hello, World!
vecpair[0].second:one
vec7:8
empty:0
size:5
1
2
3
5
8
auto:1
auto:2
auto:3
auto:5
auto:8
2
88
3
5
vec2[1]:88
vec2.at(1):88
vec2.front():2
vec2.back():5
size:0capacity:8
size:5capacity:8
vec2:8
vec2:2
vec2:3
vec2:4
vec2:5
vec5:3
vec5:4
vec5:5
vec5:0
vec5:0
sort vec5:0
sort vec5:0
sort vec5:3
sort vec5:4
sort vec5:5
anti sort vec5:5
anti sort vec5:4
anti sort vec5:3
anti sort vec5:0
anti sort vec5:0
afer reverse:
0
0
3
4
5
其他操作备忘:
- 二维数组初始化:
//1、使用 行数 + 一维数组的方式进行初始化
vector<vector<int>> vec(4,vector<int>(13,0)); //初始化一个4*13的二维数组,且数组的元素都是0
//2、使用 大括号的形式进行初始化
vector<vector<int>> vec{{1,2,3},{1,2,3}}; //初始化一个2*3的二维数组
- ==的使用
int main(){
std::vector<int> vec2={1,2,3,5};
std::vector<int> vec3(1,6);
//如果这里为{1,2,3,4},后面的判断就不会为真
//如果这里使用的是vec3[0],vec3[1]……的方法赋值,那么因为vec3长度大于vec2,且未修改部分仍为1(如果没默认值假设为0),那么后面的判断也不会为真,不会走进输出的逻辑
vec3={1,2,3,5};
if(vec2==vec3){
cout<<"vec2==vec3"<<endl;
}
}
两个容器之前使用==比较,可以发现其实是对两个容器的内容逐个比较,即使vec3的容器长度比vec2长,但内容与vec2一样,所以判断为真,会输出打印两个容器相等的逻辑
vec2==vec3
- map操作
unordered_map 是关联容器,含有带唯一键的键(key;it->first)-值(value;it->second) pair 。搜索、插入和元素移除拥有平均常数时间复杂度。
元素在内部不以任何特定顺序排序,而是组织进桶中。元素放进哪个桶完全依赖于其键的哈希。这允许对单独元素的快速访问,因为一旦计算哈希,则它准确指代元素所放进的桶。
由于unordered_map内部采用的hashtable的数据结构存储,所以,每个特定的key会通过一些特定的哈希运算映射到一个特定的位置,我们知道,hashtable是可能存在冲突的(多个key通过计算映射到同一个位置),在同一个位置的元素会按顺序链在后面。所以把这个位置称为一个bucket是十分形象的(像桶子一样,可以装多个元素)。
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<vector>
#include<list>
#include<unordered_map>
#include<algorithm>
using namespace std;
void myprint(int val){
cout<<val<<endl;
}
int main(){
unordered_map<int,string> mtable1={ {1,"This"},{2,"is"} };
// 1、移动构造函数
unordered_map<int, string> mtable2 = move(mtable1);
//返回根据系统或库实现限制的容器可保有的元素最大数量,即对于最大容器的 std::distance(begin(), end())
cout<<"mtable1.size():"<<mtable1.size()<<"-max_size:"<<mtable1.max_size()<<endl;
for(auto it=mtable1.begin();it!=mtable1.end();it++){
cout<<"mtable1:"<<it->first<<it->second<<endl;
}
// 2、范围构造函数
unordered_map<int, string> mtable3(mtable2.begin(), mtable2.end());
//map的操作
//1、插入
mtable3.insert({5,"an"});
//再次插入插不进去了
mtable3.insert({5,"qq"});
//如果insert_or_assign的值为5则会覆盖上面的key的值
mtable3.insert_or_assign(3,"apple");
mtable3.emplace(7,"U pan");
mtable3.try_emplace(8,"V pan");
//2、遍历
for(auto it=mtable3.begin();it!=mtable3.end();it++){
cout<<"mtable3:"<<it->first<<it->second<<endl;
}
//3、擦除
mtable3.erase(mtable3.begin());
for(auto it=mtable3.begin();it!=mtable3.end();it++){
cout<<"erase mtable3:"<<it->first<<it->second<<endl;
}
//4、交换两个表
mtable3.swap(mtable2);
mtable3[1]="hellow";
for(auto it=mtable3.begin();it!=mtable3.end();it++){
cout<<"mtable3:"<<it->first<<it->second<<endl;
}
//5、查找
auto item=mtable3.find(2);
cout<<"mtable3 find:"<<item->first<<"->"<<item->second<<endl;
//通过equal_range进行遍历
auto range=mtable3.equal_range(2);
for(item=range.first;item!=range.second;item++){
cout<<"mtable3 equal_range:"<<item->first<<"->"<<item->second<<endl;
}
//使用find判断map中是否有某个键
if(mtable2.find(5)!=mtable2.end()){
cout<<"find(5) in mtable2!"<<endl;
}
//下面的语句有问题,不能直接通过-1移动指针位置。
// auto at=mtable2.end()-1;
//因为end()返回的是最后一个元素边界,属于数组上沿,下面的输出就越界了,map最后一个元素应该是mtable2.end()-1
// cout<<"mtable2.end():"<<mtable2.end()->second<<endl;
//6、返回桶数
int num=mtable2.bucket_count();
cout<<"bucket_count:"<<num<<endl;
for(int i=0;i<num;i++){
cout << "buckst's load_factor:(每个桶的平均元素数量) " << mtable2.load_factor() <<" ";
cout<<"bucket_size:"<<mtable2.bucket_size(i)<<endl;
for(auto mid=mtable2.begin(i);mid!=mtable2.end(i);mid++){
cout<<"["<<mid->first<<"->"<<mid->second<<"]";
}
cout<<endl;
}
cout << "2 in the bucket" << mtable2.bucket(2) << endl;
//7、设置桶数为 count 并重哈希容器,即考虑桶总数已改变,再把元素放到适当的桶中
mtable2.rehash(2);
for (int i = 0; i < num; i++)
{
cout << "buckst's load_factor:(每个桶的平均元素数量) " << mtable2.load_factor() << " ";
cout << "bucket[" << i << "]" << ".size():" << mtable2.bucket_size(i) ;
cout << endl;
}
//8、reserve 设置桶数为适应至少 count 个元素,而不超出最大加载因子所需的数,并重哈希容器,即考虑桶数已更改后将元素放进适合的桶。
return 0;
}
对应输出
mtable1.size():0-max_size:384307168202282325
mtable3:8V pan
mtable3:7U pan
mtable3:3apple
mtable3:5an
mtable3:1This
mtable3:2is
erase mtable3:7U pan
erase mtable3:3apple
erase mtable3:5an
erase mtable3:1This
erase mtable3:2is
mtable3:2is
mtable3:1hellow
mtable3 find:2->is
mtable3 equal_range:2->is
find(5) in mtable2!
bucket_count:13
buckst's load_factor:(每个桶的平均元素数量) 0.384615 bucket_size:0
buckst's load_factor:(每个桶的平均元素数量) 0.384615 bucket_size:1
[1->This]
buckst's load_factor:(每个桶的平均元素数量) 0.384615 bucket_size:1
[2->is]
buckst's load_factor:(每个桶的平均元素数量) 0.384615 bucket_size:1
[3->apple]
buckst's load_factor:(每个桶的平均元素数量) 0.384615 bucket_size:0
buckst's load_factor:(每个桶的平均元素数量) 0.384615 bucket_size:1
[5->an]
buckst's load_factor:(每个桶的平均元素数量) 0.384615 bucket_size:0
buckst's load_factor:(每个桶的平均元素数量) 0.384615 bucket_size:1
[7->U pan]
buckst's load_factor:(每个桶的平均元素数量) 0.384615 bucket_size:0
buckst's load_factor:(每个桶的平均元素数量) 0.384615 bucket_size:0
buckst's load_factor:(每个桶的平均元素数量) 0.384615 bucket_size:0
buckst's load_factor:(每个桶的平均元素数量) 0.384615 bucket_size:0
buckst's load_factor:(每个桶的平均元素数量) 0.384615 bucket_size:0
2 in the bucket2
buckst's load_factor:(每个桶的平均元素数量) 0.714286 bucket[0].size():1
buckst's load_factor:(每个桶的平均元素数量) 0.714286 bucket[1].size():1
buckst's load_factor:(每个桶的平均元素数量) 0.714286 bucket[2].size():1
buckst's load_factor:(每个桶的平均元素数量) 0.714286 bucket[3].size():1
buckst's load_factor:(每个桶的平均元素数量) 0.714286 bucket[4].size():0
buckst's load_factor:(每个桶的平均元素数量) 0.714286 bucket[5].size():1
buckst's load_factor:(每个桶的平均元素数量) 0.714286 bucket[6].size():0
- 链表
//1、创建list容器
list<int>L1;
//2、添加数据
L1.push_back(10); //尾插
L1.push_back(20); //尾插
//3、区间方式构造
list<int>L2(L1.begin(), L1.end());
//4、拷贝构造
list<int>L3(L2);
//5、10个1000
list<int>L4(10, 1000);
L2.assign(10, 100);
//操作:
/*
-push_back(elem);//在容器尾部加入一个元素
-pop_back(elem);//删除容器中最后一个元素
-push_front(elem);//在容器首部加入一个元素
-pop_front(elem);//删除容器开头的一个元素
-insert(pos,elem);//在pos位置处插入elem元素的拷贝,返回新数据的位置
-insert(pos,n,elem);//在pos位置插入n个elem数据,无返回值
-insert(pos,beg,end);//在pos位置插入[beg,end)区间的数据,无返回值
-clear();//移除容器的所有数据
-erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置
-erase(pos);//删除pos位置的数据,,返回下一个数据的位置
-remove(elem);//删除容器中所有与elem值匹配的元素
*/
//1、遍历
for (list<int>::const_iterator it = L.begin(); it != L.end(); it++)
{
cout << *it << " ";
}
//2、调整大小
L1.resize(10);
//若容器边长则以111填充新位置
L1.resize(12, 111);
//3、获取元素
//L1[0] 不可以用[]访问list容器中的元素
//L1.at(0) 不可以用at的方式来访问list中的元素
//原因是list本质是链表,不是用连续的线性空间存储数据,迭代器不支持随机访问的
cout << "第一个元素" <<L1.front()<< endl;
cout << "最后一个元素" << L1.back() << endl;
//验证迭代器不支持随机访问的
list<int>::iterator it = L1.begin();
it++; //支持递增
it--; //支持递减
//it = it + 1; 不支持随机访问
//4、排序
//reverse();//反转链表
//sort();//链表排序
//所有不支持随机访问迭代器的容器,不可以用标准算法
//不支持随机访问迭代器的容器,内部会提供对应一些算法
cout << "升序 排序后:" << endl;
sort(L1.begin(), L1.end());
L1.sort();//默认排序规则是从小到大 升序
cout << "降序 序排序后:" << endl;
L1.sort(Compare);
//自定义的bool比较函数
bool Compare(int v1,int v2)
{
//降序 就让第一个数大于第二个数
return v1 > v2;
}
//合并两个list,按照指定的顺序排序
//void merge( list &lst, Comp compfunction );
其他备忘
- 默认构造函数:仅当类中不包含用户声明的构造函数时才生成。
- 析构函数:默认生成,当基类的析构函数为虚时,派生类的默认析构函数为虚函数。
- 拷贝构造函数:仅当类中不包含用户声明的拷贝构造函数时才生成。如果该类声明了移动操作,那么拷贝构造函数将被定义为删除的。
- 拷贝赋值运算符:仅当类中不包含用户声明的拷贝赋值运算符时才生成。如果该类声明了移动操作,那么拷贝赋值运算符将被定义为删除的。
- 移动构造函数和移动赋值运算符:仅当类中不包含用户声明的拷贝操作、移动操作和析构函数时才生成。