C++11中引出了变量的类型自动推导,它和Python 不一样,C++需要用auto关键字来引导
auto修饰变量,可以自动推导出变量的数据类型
1> 使用auto修饰变量时,必须初始化
2> auto的右值,可以是右值,也可以是表达式,还可以是函数的返回值
3> auto不能直接声明数组
4> auto不能作为函数的形参
5> auto不能修饰非静态成员
1> 用于修饰比较冗长的数据类型
2> 用于函数模板中,依赖模板参数的变量
3> 用lambda表达式中
#include
using namespace std;
int fun(int a, int b, float *c, char d, double *e,int f)
{
return 1+2;
}
int main()
{
//定义一个函数指针,指向fun函数
int (*p)(int , int , float *, char , double *,int ) = fun;
//用auto来完成
auto ptr = fun;
return 0;
}
#include
using namespace std;
template
void fun(T a)
{
auto b = a;
cout << "b的数据类型:" << typeid (b).name() << endl;
}
int main()
{
int a =10;
fun(a);
return 0;
}
当你需要用一个匿名的、临时的、需要获得外界变量的函数时,可以用lambda来完成。
[](){}
[捕获外界变量列表](形参列表){函数体内容}
[捕获外界变量列表](形参列表)->return type{函数体内容}
[] :捕获外界变量列表
[变量1,变量2,变量3,...,变量n] :对外界的变量进行值捕获,捕获的变量和外界变量值一样,地址不一样,并且值不可以被修改。
如果想对值进行修改,加上mutable关键字
[=] :外界所有的变量进行值捕获。
[&变量1,&变量2,&变量3,...,&变量n]:对外界的变量进行引用(址)捕获,获的变量和外界变量值一样,地址一样
这个时候就不需要加mutable
[&] :外界所有的变量进行引用捕获
[=,&变量1,&变量2]
[&,变量1,变量2]
() :形参列表
->return type 返回类型
#include
using namespace std;
int main()
{
int a = 100;
double b = 13.45;
char c = 'x';
cout << "main-&a:" << &a << " a = " << a << endl;
// auto fun = [a,b,c]()mutable{
// auto fun = [=]()mutable{
auto fun = [&a,&b](){
cout << "lambda--&a " << &a << " a = " << a << endl;
a = 200;
cout << "lambda--&a " << &a << " a = " << a << endl;
};
fun();
cout << "main-&a:" << &a << " a = " << a << endl;
return 0;
}
#include
using namespace std;
int main()
{
int a = 100;
double b = 13.45;
char c = 'x';
cout << "main-&a:" << &a << " a = " << a << endl;
// auto fun = [a,b,c]()mutable{
// auto fun = [=]()mutable{
auto fun = [&a,&b](string name = "hello world")->string{
cout << "lambda--&a " << &a << " a = " << a << endl;
a = 200;
cout << "lambda--&a " << &a << " a = " << a << endl;
return name;
};
cout << fun("hello kitty") << endl;
cout << "main-&a:" << &a << " a = " << a << endl;
return 0;
}
C++中存在多种数据类型的转换方式,用于在不同的数据类型之间进行转换。
以下是常见的数据类型转换方式:
这是C++编译器自动执行的类型转换,通常在表达式中出现时发生。例如,将较小的整数转换为较大的整数类型,将整数提升为浮点数等。
int num_int = 10; double num_double = num_int; // 隐式将int转换为double
通过使用强制类型转换操作符来显示执行类型转换。这种转换可能会导致数据的截断或者精度丢失,因此要小心使用。
用于基本数据类型之间的转换
以及父类指针/引用转换为子类指针/引用
还可以用于不同类型的指针之间的转换
double num_double = 3.14;
int num_int = static_cast(num_double); // 显式将double转换为int
(2)动态转换(dynamic_cast)
通常用于多态类之间的指针或引用类型转换,确保类型安全。在运行时进行类型检查,只能用于具有虚函数的类之间的转换
class Base {
virtual void foo() {}
};
class Derived : public Base {};
Base* base_ptr = new Derived;
Derived* derived_ptr = dynamic_cast(base_ptr); // 显式将基类指针转换为派生类指针
用于添加或移除指针或引用的常量性。它可以用来去除const限定符,但要注意潜在的未定义行为
const int a =10; //
int *p;
p = &a; // 合不合法? no
const int num_const = 5;
int* num_ptr = const_cast(&num_const); // 去除const限定符
执行低级别的位模式转换,通常用于指针之间的类型转换。它可能导致未定义行为,因此要谨慎使用
int num = 42;
float* float_ptr = reinterpret_cast(&num); // 重新解释转换
与C语言中的类型转换方式类似,包括以下几种:
- c样式转换:使用强制类型转换 操作符进行转换,类似与C语言中的类型转换
int num_int = 10; double num_double = (double)num_int; // C样式强制类型转换
- 函数样式转换(函数式转换):使用C++中的类型转换函数进行转换
int num_int = 10; double num_double = double(num_int); // C++函数样式类型转换
需要注意的是,尽管C++提供了多种类型转换方式,但应该谨慎使用,以避免潜在的错误和问题。特别是在使用强制类型转换时,务必确保转换操作是安全的,以避免不必要的问题。
1> C++中一共有63个关键字,如上图所示,其中标红的为c语言中的关键字,有32个
:这是一个用于嵌入汇编语言代码的关键字。它允许你在C++代码中直接插入汇编指令,通常用于执行特定的底层操作。然而,由于现代C++提供了更强大的抽象和跨平台性,通常不建议使用这个关键字。
:这个关键字通常用于禁止隐式类型转换的发生。当一个构造函数被声明为explicit时,它将不会在隐式类型转换中被调用,只能在显式构造函数调用中使用。
:在C++中,export关键字用于指示一个模板的定义将在另一个文件中实例化。然而,在实际的C++标准中,export关键字的语法并未最终确认,并且在许多编译器中也未被实现。在C++20中,export被重新引入,但是它的主要用途是与模块化编程相关,而不是之前模板实例化的用法。
:goto是一个跳转语句,允许你无条件地将程序的控制转移到指定的标签处。然而,由于使用goto会导致代码结构变得混乱和难以维护,现代编程实践通常建议避免使用它。
:在早期的C语言标准中,register关键字用于建议编译器将变量存储在寄存器中,以便提高访问速度。然而,现代编译器已经能够智能地管理寄存器分配,所以使用register关键字通常不再有明显的性能提升,并且在C++17中已被弃用。
:volatile关键字用于告诉编译器不要对标记为volatile的变量进行优化,因为这些变量的值可能会在未知的时间被外部因素改变,比如硬件中断或多线程环境中的共享变量。这可以防止编译器对这些变量的读取和写入操作进行优化,以确保程序的行为是可预测的。
2> 数据类型相关的关键字
bool、true、false:对于bool类型数据的相关处理,值为true和false
char、wchar_t:char是单字符数据,wchar_t多字符数据
int、short、float、double、long:整数和实数的数据类型
signed、unsigned:定义有符号和无符号数据的说明符
auto:在c语言中,是存储类型,但是在C++中,是类型自动推导,注意事项有两个:
i> 连续定义多个变量时,初始值必须是相同数据类型,否则报错
ii> auto p=&m; 与auto* p = &m;规定是一样
explicit:防止数据隐式转换
typedef:类型重定义
sizeof:求数据类型的字节运算
3> 语句相关的关键字
switch、case、default:实现多分支选择结构
do、while、for:循环相关的关键字
break、continue、goto:跳转语句
if、else:选择结构
inline:内联函数
return:函数返回值
4> 存储类型相关的关键字
static、const、volatile、register、extern、auto
5> 构造数据类型相关
struct、union:结构体和共用体
enum:枚举
class:类
6> 访问权限:public、protected、private
7> 异常处理:throw、try、catch
8> 类中相关使用关键字
this:指代自己的指针
friend:友元
virtual:虚
delete、default:对类的特殊成员函数的相关使用
例如:Test(const Test &) = default; ~Test() = delete;
mutable:取消常属性
using:引入数据,有三种使用方式
i> 使用命名空间的关键字
ii> 相当于类型重定义
iii> 修改子类中从父类继承下来成员的权限
operator:运算符重载关键字
9> 类型转换相关的关键字
static_cast、dynamic_cast、const_cast、reinterpret_cast
10> 模板相关的关键字:template、typename
11> 命名空间相关:using、namespace
12> export:导入相关模板类使用
13> 内存分配和回收:new、delete
C++中的标准模板库(Standard Template Library)STL, 是标准库之一。
标准模板库中使用了大量的函数模板和类模板,来对数据结构和算法的处理。
STL主要由 容器、算法、迭代器组成。
容器:置物之所也
数组、链表、队列、栈、集合。。。
算法:问题之解法也
增、删、改、查
迭代器:是容器和算法之间的粘合剂 (≈≈ 指针)
vector类似于数组,也可以称为单端数组。
vector和普通数组的区别,普通数组是静态空间,vector是动态拓展。
动态拓展:不是在原来空间后续接空间,而是重新申请更大的空间,将原来的数据拷贝到新的空间中。
函数原型:
vector v;//无参构造
vector(v.begin(),v.end()); //将区间[begin(),end()),拷贝给本身
vector(const vector& v); //将v拷贝给本身
vector(n, elem) ; //将n个elem拷贝给本身
#include
#include
using namespace std;
//算法
void printVector(vector &v)
{
vector::iterator iter; //定义了这样容器类型的迭代器
for(iter = v.begin(); iter != v.end(); iter++)
{
cout << *iter << " ";
}
cout << endl;
}
int main()
{
//容器
vector v; //无参构造函数
v.push_back(10); //尾插
v.push_back(20);
v.push_back(30);
v.push_back(40);
//算法
printVector(v);
vector v2(v.begin(),v.end());
printVector(v2);
vector v3(6,100);
printVector(v3);
vector v4 = v3;
printVector(v4);
vector v5(v2);
printVector(v5);
return 0;
}
函数原型:
vector& operator=(const vector &v);//将v赋值给本身
assign(beg,end); //将区间[begin(),end())赋值给本身
assign(n, elem); //将n个elem赋值给本身
函数原型:
empty(); //判断容器是否为空
capacity() ; //计算容器的容量大小
size(); //计算容器大小 ---- 容器的元素个数
resize(); //重新设置大小
#include
#include
using namespace std;
//算法
void printVector(vector &v)
{
vector::iterator iter; //定义了这样容器类型的迭代器
for(iter = v.begin(); iter != v.end(); iter++)
{
cout << *iter << " ";
}
cout << endl;
}
int main()
{
//容器
vector v; //无参构造函数
v.push_back(10); //尾插
v.push_back(20);
v.push_back(30);
v.push_back(40);
//算法
printVector(v);
vector v2(v.begin(),v.end());
printVector(v2);
vector v3(6,100);
printVector(v3);
vector v4 = v3;
printVector(v4);
vector v5(v2);
printVector(v5);
vector v6;
v6 = v5;
printVector(v6);
v6.assign(v5.begin(),v5.end());
printVector(v6);
v6.assign(8,99);
printVector(v6);
if(v6.empty())
{
cout << "容器为空" << endl;
}
else
{
cout << "容器的容量大小:" << v6.capacity() << endl;
cout << "容器的大小:" << v6.size() << endl;
v6.resize(15);
printVector(v6);
}
return 0;
}
函数原型:
push_back(); //尾插
pop_back(); //尾删
insert(iterator pos,elem) //在迭代器指向的位置,插入数据
insert(iterator pos,n, elem) //在迭代器指向的位置,插入n个数据
erase(iterator pos); //删除迭代器指向元素
erase(iterator start, iterator end); //删除区间的元素
clear() ; //删除容器中所有的元素
函数原型:
at(int idx);
operator[](int idx);
front(); //返回第一个元素
back(); //返回最后一个元素
#include
#include
using namespace std;
//算法
void printVector(vector &v)
{
vector::iterator iter; //定义了这样容器类型的迭代器
for(iter = v.begin(); iter != v.end(); iter++)
{
cout << *iter << " ";
}
cout << endl;
}
int main()
{
//容器
vector v; //无参构造函数
v.push_back(10); //尾插
v.push_back(20);
v.push_back(30);
v.push_back(40);
//算法
printVector(v);
// v.pop_back();//尾删
// printVector(v);
// v.insert(v.begin()+1,99);
// printVector(v);
// v.insert(v.begin(),4,77);
// printVector(v);
// v.erase(v.begin());
// printVector(v);
// v.erase(v.begin(),v.end());
// printVector(v);
v.clear();
printVector(v);
cout << "------------------" < vv;
for(int i=0;i<5;i++)
{
vv.push_back(i);
}
cout << vv.at(3) << endl;
cout << vv[3] << endl;
cout << vv.front() << endl;
cout << vv.back() << endl;
return 0;
}
功能:
将数据进行链式存储
链表(list) 是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的。
链表的组成:
链表由一系列节点组成
节点的组成:
一个是存储数据元素的数据域,另一个是存储下一个节点地址的指针域
STL中的链表是一个双向链循环链表
list的优点:
- 采用动态存储分配,不会造成内存浪费和溢出
- 链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素
list缺点:
- 链表灵活,但是空间(指针域)和 时间(遍历)额外消耗比较大
函数原型:
- list lst; //无参构造函数
- list(beg, end); //构造函数将[begin,end)区间中对的元素拷贝给本身
- list(n, elem); //构造函数将n个elem拷贝给本身
- list(const list& l); //拷贝构造函数
函数原型:
- assign(beg, end); //将[beg,end)区间中的数据拷贝赋值给本身
- assign(n, elem); //将n个elem拷贝赋值给本身
- list& operator=(const list &lst); //重载赋值符
- swap(lst); //将lst与本身的元素互换
函数原型:
- size(); //返回容器中元素的个数
- empty(); //判断容器是否为空
- resize(num); //重新指定容器的长度为um,若容器变长,则以默认值填充新空间
- //如果容器变短,则末尾超出容器长度的元素则被删除
- resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新空间
//如果容器变短,则末尾超出容器的元素被删除。
函数原型:
- push_back(); //尾部插入元素
- pop_back(); //删除最后一个元素
- push_front(elem); //在容器的开头插入一个元素
- pop_front(); //在容器的开头删除一个元素
- insert(const_iterator pos, ele); //迭代器指向位置pos插入元素ele
- insert(const_iterator pos, int count, ele); //迭代器指向位置pos 插入count个元素ele
- insert(pos,beg, end); //在pos位置插入[beg,end)区间的数据,无返回值。
- erase(const_iterator pos); //删除迭代器指向的元素
- erase(const_iterator start, const_iterator end); //删除迭代器从start到end之间的元素
- clear(); //删除容器中所有的元素
- remove(elem); //删除容器中所有与elem值匹配的元素。
函数原型:
- front(); //返回第一个元素
- back(); //返回最后一个元素
程序运行时产生的数据都是临时数据,程序一旦结束,数据就会消失
文件可以将数据持久化
文件相关操作的头文件 #include
文件操作的三大类:
读文件 : ifstream
写文件:ofstream
读写:fstream
1.包含头文件
#include
2.创建流对象
ofstream ofs;
3.打开文件
ofs.open("文件名",打开方式);
文件的打开方式
4.写入数据
ofs
5.关闭文件
ofs.close();
1.包含头文件
#include
2.创建流对象
ifstream ifs;
3.打开文件
ifs.open("文件名",打开方式);
4.读取数据
ifs >>
5.关闭文件
ifs.close();
#include
#include
using namespace std;
int main()
{
//1包含头文件
//2创建流对象
ofstream ofs;
//3打开文件
ofs.open("E:/ready_class/stu.txt",ios::out);
//4写入数据
ofs << "姓名:张三" << endl;
ofs << "年龄:34" << endl;
//5关闭文件
ofs.close();
//1包含头文件
//2创建流对象
ifstream ifs;
//3打开文件
ifs.open("E:/ready_class/stu.txt",ios::in);
//4读取数据
char buff[1024];
while(ifs>>buff)
{
cout << buff << endl;
}
//5.关闭文件
ifs.close();
return 0;
}
封装一个学生的类,定义一个学生这样类的vector容器, 里面存放学生对象(至少3个)
再把该容器中的对象,保存到文件中。
再把这些学生从文件中读取出来,放入另一个容器中并且遍历输出该容器里的学生。
#include
#include
#include
using namespace std;
class Student{
public:
string name;
int ID;
string sex;
public:
Student(){}
Student(string n, int i, string s):name(n),ID(i),sex(s){}
Student & operator=(const Student &other){
name = other.name;
ID = other.ID;
sex = other.sex;
return *this;
}
};
ofstream &operator <<(ofstream &ofs, const Student &stu)
{
ofs << stu.name << " " << stu.ID << " " << stu.sex;
return ofs;
}
ifstream &operator >>(ifstream &ifs, Student &stu)
{
ifs >> stu.name >> stu.ID >> stu.sex;
return ifs;
}
ostream & operator<<(ostream &cout, const Student &stu)
{
cout << stu.name << stu.ID << stu.sex << endl;
return cout;
}
int main()
{
Student stu1("张三",1001,"男");
Student stu2("李四",1002,"男");
Student stu3("王五",1003,"男");
vector v;
v.push_back(stu1);
v.push_back(stu2);
v.push_back(stu3);
ofstream ofs;
ofs.open("C:\\Users\\29154\\Desktop\\23HQYJ\\C++\\ceshi.txt",ios::out);
vector::iterator ite;
for(ite = v.begin(); ite != v.end(); ite++)
{
ofs << *ite << endl;
}
ofs.close();
ifstream ifs;
Student stu[100];
vector v1;
ifs.open("C:\\Users\\29154\\Desktop\\23HQYJ\\C++\\ceshi.txt",ios::in);
int i = 0;
while (ifs >> stu[i]) {
v1.push_back(stu[i]);
i++;
}
vector::iterator it;
for(it = v1.begin(); it != v1.end(); it++)
{
cout << *it << endl;
}
ifs.close();
return 0;
}