201_C++基础

一、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

二、容器操作备忘

  1. 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

  1. 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. 链表
    //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 ); 

其他备忘
  • 默认构造函数:仅当类中不包含用户声明的构造函数时才生成。
  • 析构函数:默认生成,当基类的析构函数为虚时,派生类的默认析构函数为虚函数。
  • 拷贝构造函数:仅当类中不包含用户声明的拷贝构造函数时才生成。如果该类声明了移动操作,那么拷贝构造函数将被定义为删除的。
  • 拷贝赋值运算符:仅当类中不包含用户声明的拷贝赋值运算符时才生成。如果该类声明了移动操作,那么拷贝赋值运算符将被定义为删除的。
  • 移动构造函数和移动赋值运算符:仅当类中不包含用户声明的拷贝操作、移动操作和析构函数时才生成。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值