1.typeid(*).name() -----(类型的查看)
l类型 | 显示 |
---|---|
int | i |
float | f |
char* | PKc (pointer–const–char) |
int arr[] | typeid(arr).name() |
typeid(string).name() | 好长的字符串(string); |
typeid(vector).name() | 好长的字符串(vector) |
typeid(Simple).name() | 类的话(*个字符) |
auto it = vec.begin();cout << typeid(it).name() << endl; | 好长的字符串(iterator) |
func | func |
注意:
(1)auto 进行变量定义必须初始化;
auto m1= 10;//必须要初始化;
(2)auto不能推导数组;
auto m4[] = {
1,2,3,4,5,6};
cout << typeid(m4).name() << endl;
2.基于范围的for循环
for(auto a:arr){
cout << a << " ";
}
cout << endl;
3.定义迭代器
vector<int> vec = {
1,2,3};
//vector::iterator it = vec.begin(); //不推荐
auto it = vec.begin(); //推荐
cout << typeid(it).name() << endl;
(4) 定义函数指针
auto func = Func;
func();
cout << typeid(func).name() << endl;
完整代码见001——auto.cpp
#include
#include
#include
using namespace std;
class Simple /*final*/{
// 禁止继承
};
void Func(){
cout<< __func__<<endl;
}
int main(){
// auto 自动推导变量类型
int n = 10;
cout << typeid(n).name() << endl;//i
float f;
cout << typeid(f).name() << endl; //f
const char* hello = "abc";
cout << typeid(hello).name() << endl; //PKc (pointer--const--char)
int arr[] = {
1,2,3,4,5};
cout << typeid(arr).name() << endl;//Ai5(array_int_5)
cout << typeid(string).name() << endl; //好长的字符串(string);
cout << typeid(vector<int>).name() << endl;//好长的字符串(vector);
cout << typeid(Simple).name() << endl; //类的话(6个字符)
auto m1= 10;//必须要初始化;
auto m2= 10.0f;
cout << typeid(m1).name() << endl; //i;
cout << typeid(m2).name() << endl; //f;
auto m3 = "abc";
cout << typeid(m3).name() << endl; //PKc (pointer--const--char)
//auto m4[] = {1,2,3,4,5,6};
//cout << typeid(m4).name() << endl;
//(1)注意:不能推导出数组
// (2)基于范围的for循环
for(auto a:arr){
cout << a << " ";
}
cout << endl;
// (3)定义迭代器
vector<int> vec = {
1,2,3};
//vector::iterator it = vec.begin();
auto it = vec.begin();
cout << typeid(it).name() << endl;好长的字符串(iterator);
// (4)定义函数指针
auto func = Func;
func();
cout << typeid(func).name() << endl; //FUNC
}
(1) nullptr表示c++里面的空指针
(2)C中NULL和C++中的NULL是由区别的
(c里面:#define NULL ((void*)0
)
c++:#define NULL 0
)
所以c++里面再分配内存的时候,代表的是0,需要强制转化为指针;
(3) NULL定义在stddef.h
完整代码见002_nullptr.cpp
#include
#include
#include
using namespace std;
void Test(int* p){
cout << "int*" << endl;
}
void Test(int n){
cout << "int" << endl;
}
int main(){
// nullptr表示空指针
// C中NULL和C++中的NULL是由区别的(c里面:#define NULL ((void*)0) c++:#define NULL 0 )所以c++里面再分配内存的时候,代表的是0,需要强制转化为指针;
// NULL定义在stddef.h
// Test(NULL);
Test(nullptr);
Test(0);
}
lambda表达式
捕获列表->返回值类型{执行语句;}捕获的方式比较任意,最错误的写法:全用=
(1)值形式捕获,外边的数值不发生变化;默认用=代替;必须指定特殊的;引用进行捕获并修改;默认用&;必须指定特殊的;
for_each(vec.begin(),vec.end(),[&res,num](int& n){
res.push_back(n*n+num);});
完整代码见003_lamada.cpp
#include
#include
#include
using namespace std;
void Func(){
cout << __func__ << endl;
}
void Test(int* p){
cout << "int*" << endl;
}
void Test(int n){
cout << "int" << endl;
}
int main(){
// 定义迭代器
vector<int> vec = {
1,2,3};
//vector::iterator it = vec.begin();
auto it = vec.begin();
cout << typeid(it).name() << endl;
// lambda表达式
// [捕获列表](参数列表)->返回值类型{执行语句;}捕获的方式比较任意,最错误的写法:全用=
// (1)值形式捕获,外边的数值不发生变化;默认用=代替;必须指定特殊的;
for_each(vec.begin(),vec.end(),[](int n){
cout <<n<<",";});
cout << endl;
// 引用形式捕获
int sum = 0;
for_each(vec.begin(),vec.end(),[&sum](int n){
sum += n;}); //(2)引用进行捕获并修改;默认用&;必须指定特殊的;
cout << sum << endl;
int num = 100;
vector<int> res;
for_each(vec.begin(),vec.end(),[&res,num](int& n){
res.push_back(n*n+num);});
for_each(res.begin(),res.end(),[](int n){
cout <<n<<",";});
cout << endl;
}
关键字 | 功能 | example |
---|---|---|
default | 使用默认函数 | Simple() = default; |
delete | 禁止 | Simple(const Simple&) = delete; |
final | (1)加上final,禁止继承;注释掉,可以继承 | class Simple /*final*/ |
final | (2)禁止重写 | virtual void Funcl() const /*final*/ |
override | 表示当前函数是重写父类的虚函数,避免重写的函数名出错的问题; | virtual void Funcl()const override |
完整代码见004_hot_words.cpp
#include
#include
#include
using namespace std;
class Simple /*final*/{
// (3.1)加上final,禁止继承;注释掉,可以继承
public:
Simple() = default; // (1)使用默认构造函数
Simple(const Simple&) = delete;//(2)禁止拷贝
virtual void Funcl() const /*final*/{
// (3.2)禁止重写/覆盖
}
};
class SubSimple:public Simple{
virtual void Funcl()const override{
// (4)override表示当前函数是重写父类的虚函数,避免重写的函数名出错的问题;
}
};
int main(){
return 0;
}
左值右值一直存在,只从右值引用出现,这对CP变得重要起来,那么为什么要有右值引用?没有右值引用C++不是活得好好的吗?一切要从左值引用说起。
在C++11之前,没有左值引用与右值引用之分,引用专指左值引用。那个时候就出现了不和谐的情况。
#include
using namespace std;
void Print(string& s){
cout << s;
}
int main(){
string s="abc";
Print(s); // OK
Print("abc"); // parse error
}
函数Print()
能够打印s
,却不能打印"abc"
。
自定义对象也会出现这种情况
#include
using namespace std;
class Demo{
};
void Func(Demo &){
cout << "Func(Demo &)" << endl;
}
int main(){
Demo d;
Func(d); // OK
Func(Demo()); // parse error
}
在上面代码中,Func(d);
编译执行都OK,而Func(Demo());
编译出错。
如果说上一个是因为类型不匹配(一个是string
,一个是char []
),这一次都是Demo
对象。同样都是对象怎么待遇不一样?问题就在于d
是左值(有名对象),Demo()
是右值(匿名对象/临时对象)。
那么,之前为什么很多应用过程中没有大规模的出现这个问题?
因为C++对const&
添加特殊技能:既能接受左值又能接受右值。上面两个例子把函数参数加上const
,即void Print(const string& s)
和void Func(const Demo &)
,问题解决了。
void Func(const Demo &)
类型匹配解决了,为什么void Print(const string& s)
也可以了呢?因为编译器自动把根据函数参数类型string
,自动为"abc"
构造一个临时对象string("abc")
,这是默认类型转换。
但是这种解决方式只是解决了参数不变的情况,如果参数可变问题依然存在。例如:
有一个用户类,记录用户名、密码和登录次数。
struct User{
string name;
string pass;
int count;
}
提供一个函数bool Login(User& user);
登录成功后,返回true
,并且修改登录次数count
。
bool Login(User& user) {
// 此处略去验证
user.count++;
return true
}
上面情况只能使用变量,不能使用右值(匿名对象),否则会出现上面的错误。但这种情况是存在的,只关注用户能否登录,不关心登录次数,使用右值(匿名对象),无可厚非,只是C++11之前语法不支持(实际中遇到这种请款,可以改设计改接口。)。于是,右值引用可以解决这种问题。重载上面的函数。
bool Login(User&& user) {
// 此处略去验证
user.count++;
return true
}
这样Login()
既可以接受左值又可以接受右值。
#include
using namespace std;
class Demo{
};
void Func(const Demo &){
cout << "Func(const Demo &)" << endl;
}
int main(){
Demo d;
Func(d);
Func(Demo());
const Demo cd;
Func(cd);
}
#include
using namespace std;
class Demo{
};
void Func(const Demo &){
cout << "Func(const Demo &)" << endl;
}
void Func(Demo &){
cout << "Func(Demo &)" << endl;
}
int main(){
Demo d;
Func(d);
Func(Demo());
const Demo cd;
Func(cd);
}
#include
using namespace std;
class Demo{
};
void Func(const Demo &){
cout << "Func(const Demo &)" << endl;
}
void Func(Demo &){
cout << "Func(Demo &)" << endl;
}
void Func(Demo &&){
cout << "Func(Demo &&)" << endl;
}
int main(){
Demo d;
Func(d);
Func(Demo());
const Demo cd;
Func(cd);
}
上面虽然把问题解决了,但是你会发现为了解决右值问题,每个函数都要写两边,并且函数名和函数体都一样,只是参数类型有区分,一个是左值,一个是右值。问题变得更加麻烦了。于是,在模板语法上添加了一个完美转发的语法。(C++滚雪球式添加新的语法,导致C++愈来愈复杂。)
上面两个Login()
函数可以合并成
template<class T>
bool Login(T&& user) {
// 此处略去验证
user.count++;
return true
}
注意,这里的&&
已经不是右值引用了,而是被称为万能引用(universal references),而这种用法称为完美转发。
当传入右值时,模板特化成bool Login(User&& user)
,当传入左值时,模板特化成bool Login(User& user)
。瞬间解决上面的问题。
&&
在模板参数中表示万能引用,在非模板参数中表示右值引用。