C/C++11| |小语法与知识点

17 篇文章 0 订阅

C/C++11的小语法与知识点


1.auto

  • 在c/c++早期的时候,auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量

  • auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器。auto声明的变量必须由编译器在编译时期推导而得的

//必须在新的编译器才可以跑过,旧的会出错
​
#include <iostream>
using namespace std;
​
int TestAuto()
{
  return 10;
}
​
int main()
{
  int a = 10;
  auto b = a;\\b是int类型
  auto c = 'a';\\c是char类型
  auto d = TestAuto();\\d是int类型
​
  cout << typeid(b).name() << endl;
  cout << typeid(c).name() << endl;
  cout << typeid(d).name() << endl;
​
​
  //auto e; 无法通过编译,使用auto定义变量时必须对其进行初始化
​
  return 0;
}

【注意】

  • 使用auto定义变量时必须对其进行初始化,在编译阶段编译器根据初始化表达式来推导auto的实际类型,因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”。编译器在编译期间会将auto替换为变量实际的类型。

 

1.1 auto与指针连用

  • auto与指针和引用结合起来使用

    用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&

#include <iostream>
using namespace std;
​
int main()
{
  int x = 10;
  auto a = &x;//int*
  auto* b = &x;//int*
  auto& c = x;//int,引用也是x,所以类型也是int
​
  cout << typeid(a).name() << endl;
  cout << typeid(b).name() << endl;
  cout << typeid(c).name() << endl;
​
  *a = 20;
  *b = 30;
  c = 40;
​
  return 0;
}
  • 同一行定义多个变量

    当在同一行定义多个变量时,这些变量必须是相同类型的,否则编译器会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量

void TestAuto()
{
    auto a = 1, b = 2;
    auto c = 3, d = 4.0;//改行代码会编译失败,因为c和d的初始化表达式类型不一样
}

 

 

auto不能推导的场景

1.auto不能作为函数的参数

//此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导
​
void TestAuto(auto a)
{
    
}

2.auto不能直接用来声明数组

void TestAuto()
{
    int a[] = { 1, 2, 3 };
    auto b[] = { 1, 2 };
}

3.auto不能定义类的非静态成员变量

4.实例化模板时不能使用auto作为模板参数

 

2. nullptr和nullptr_t

  • 为了考虑兼容性,c++11并没有消除常量0的二义性,c++11给出了全新的nullptr表示空值指针,c++11为什么不在NULL的基础上进行扩展,这是因为NULL以前就是一个宏,而且不同的编译器商对于NULL的实现可能不太相同,而且直接扩展NULL,可能会影响以前旧的程序,因此,为了避免混淆,c++11提供了nullptr,即:nullptr代表一个指针空值常量。nullptr是有类型的,其类型是nullptr_t,仅仅可以被隐式转化为指针类型,nullptr_t被定义在头文件中:

typedef decltype(nullptr) nullptr_t;

【注意】:

  1. 在使用nullptr表示指针空值的时候,不需要包含头文件,因为nullptr是c++11作为关键字引入的

  2. 在c++11中,sizeof(nullptr) 和sizeof(void* 0)所占字节数相同

  3. 为了提高代码的健壮性,在后续表示空值指针是建议最好使用nullptr

 

3. 在linux下显式的使用c++11

  • 对于一般的linux下的g++编译器都是支持c++98的,如果想让其支持c++11编译的话,那就只需要显式打开c++11就好了

#include <iostream>
#include <typeinfo>
using namespace std;
​
int TestAuto()
{
    return 10;
}
​
class Test
{
public:
    Test(int a = 0)
        : _a(a)
    {}
​
private:
    int _a = 1;//c++11支持对于非静态成员变量直接进行初始化
};
​
int main()
{
    int a = 10;
    auto b = a;
    auto c = 'a';
    auto d = TestAuto();
    Test e;
    auto f = e;
​
    cout << typeid(b).name() << endl;//int
    cout << typeid(c).name() << endl;//char
    cout << typeid(d).name() << endl;//int
    cout << typeid(e).name() << endl;//class Test
    cout << typeid(f).name() << endl;//class Test
​
​
    //auto e; 无法通过编译,使用auto定义变量时必须对其进行初始化
​
    return 0;
}

对于上面的代码,具有c++11的新语法auto,如何对其进行编译呢?

g++ -o auto auto.cpp 
//采用这种编译的话,就显示
//‘b’ does not name a type
//表示该编译器不支持c++11,所以就要显式的使用c++11编译
​
g++ -std=c++11 -o auto auto.cpp
//这种进行编译的话就不会出错,显式的使用c++11

 

 

4. typeinfo 库

包含typeid().name()工具函数,用来返回变量的数据类型,学习这个函数

typeid操作符,其返回结果名为type_info标准库类型的对象引用type_info存储特定类型的有关信息,定义在typeinfo头文件

typeid().name(),用来获取表达式的类型,以c-style字符串形式返回引用名

【注意】:对于非引用类型,typeid().name()在编译时期识别,只有引用类型才会在运行时识别。

例:

#include <iostream>
#include <typeinfo>
using namespace std;
​
int TestAuto()
{
    return 10;
}
​
class Test
{
public:
    Test(int a = 0)
        : _a(a)
    {}
​
private:
    int _a = 1;//c++11支持对于非静态成员变量直接进行初始化
};
​
int main()
{
    int a = 10;
    auto b = a;
    auto c = 'a';
    auto d = TestAuto();
    Test e;
    auto f = e;
​
    cout << typeid(b).name() << endl;//int
    cout << typeid(c).name() << endl;//char
    cout << typeid(d).name() << endl;//int
    cout << typeid(e).name() << endl;//class Test
    cout << typeid(f).name() << endl;//class Test
    //返回值是char const *,表示该函数的返回值是一个字符串(字符指针)
    cout << typeid(typeid(f).name()).name() << endl;//char const *
​
​
​
    //auto g; 无法通过编译,使用auto定义变量时必须对其进行初始化
​
    return 0;
}

【面试题】:

  • 对于可以使用typeid().name()函数的返回值做为类型名进行定义吗?

    • 不可以

      • 由于typeid().name()函数返回的是一个字符串,肯定不能用于定义

 

5. 内置类型

  • 即某个语言初期的时候定义的类型就叫做内置类型

 

6. delete函数(c++11)

//声明该函数,但是不需要进行实现
HeapType (const HeapType& ht) = delete;

 

7. 匿名对象

//生命周期只有一行
Date d1;//正常对象
Date();//这就是匿名对象

 

8. 定制化的为一个类重载operator new和operator delete

//如果你重载了operator new和operator delete 
//那么该类使用new的时候就会使用你写的operaor new和operator delete
void* operator new(size_t sizt) = delete;
viod operator delete(void* p) = delete;
//没有禁用掉全局和静态的对象产生
​
//对于其他对象new时,不会影响其他对象的new
​

 

9. size_t

无符号整数类型

size_t是sizeof运算符返回的类型,广泛应用于标准库中以表示大小和计数

 

10. C++不加.h

对于C++加不加.h,其实都是可以的,C++的标准是不需要加.h的,比如C语言中的stdio.h和string.h,在C++中就是stdio和string或者cstdio和cstring。加了C之后表示该头文件是从C语言演变过来的,对于C的那是字符串函数,而对于C++那是string对象

 

11. getline的用法

//从流cin中读取数据,写到str中,直到遇到delim
istream& getline(istream& cin, string& str, char delim);
​
//从流中读取数据写到str,默认遇到空格或者换行终止
istream& getline(istream& cin, string& str);

【作用】

​ 对于该全局函数,包含在string头文件中

​ 从流中读取数据,是不会读取到分隔符,只会讲终止符前面的数据读取到str中

 

12. cin.getline的用法

//默认的分隔符为空格或者换行
istream& getline(char* s, streamsize n);//streamsize是有符号字符大小
​
//分隔符为delim
istream& getline(char* s, streamsize n, char delim); 

【作用】

​ 对于该函数是属于cin的

​ 直接从cin中读取字符,直到遇到分隔符停止,或者已经将n个字符写入到s中,实际是读入n-1个字符,最后一个是终止符。(包括终止定界符)

 

13. 为什么main函数会有返回值

平常看到的mian函数都会有着自己的类型,都是int类型

int main()
{
    return 0;
}

对于main函数为什么要有着自己的返回值

  • 由于main函数也是一个进程运行起来的话,对于一个进程当其运行的时候,那么该进程肯定就会有着自己的父进程,那么当main函数结束的时候,父进程就要知道main函数的退出状态,所以就要有着返回值了。对于这个返回值一般都是0,返回0的话,默认该进程执行成功,正确返回。

  • 所以main函数的返回值一般都是0(return 0)

  • 如果不写return 0的话,那么对于c99之后的编译器都会自己默认返回0

 

14. 测试vector的增容情况

//测试vector自我开辟容量的
#include <iostream>
#include <vector>
​
int main()
{
  std::vector<int> varray;
  size_t sz = varray.capacity();
  
  std::cout << "test vector capacity!" << std::endl;
  for (size_t i = 0; i < 1000; i++)
  {
    varray.push_back(i);
​
    if (varray.capacity() != sz)
    {
      sz = varray.capacity();
      std::cout << "capacity change:" << sz << '\n';
    }
  }
​
  return 0;
}
  • 对于在g++下和VS2017下STL库中的vector容器增容的方法是不一样的:

  • 对于g++下:(SGI版本的STL)

    • vector是按照2倍增容的

  • 对于VS下:(PJ版本的STL)

    • vector是按照1.5增容的

 

15. 对于for+auto遍历容器

对于使用for+auto语法糖 遍历一个容器的时候需要什么东西呢?

首先肯定是需要迭代器的。

  • 但是都需要什么迭代器呢?

  • 今天突然写到这个的一个代码。就是对于vector容器的自我实现

  • 由于vector容器实现的时候需要三个模板指针,_start, _finish, _endofstorage

  • 这三个模板类型的指针,分别代表的意思是

    • _start:vector容器的起始地址

    • _finish:vector容器所包含大小的下一个地址也就是, _start+size();

    • _endofstorage:vector容器的最大容量的下一个地址, _start + capacity();

  • 那么我写了这个的一个代码。写了一个赋值运算符重载

  • 我写的时候,忘了对于_finish进行增加,导致最后的时候,_finish和 _satrt是相等的,都指向该vector容器的开头,

  • 然后我再用三种不同的方法进行遍历:

    • for+auto(需要使用迭代器)

    • for+[](不需要使用迭代器)

    • for+iterator(需要使用迭代器)

  • 这三种方法分别遍历:

  • for + iterator遍历for+auto进行出错。对于for + [],这个肯定是正确的

  • 说明对于for+iterator和for+auto都是需要begin()迭代器和end()迭代器

 

16. 在一个类型后面直接加上()表示啥意思

对于这个()表示的的意思就是创建了一个对象(匿名对象)。并且将该对象初始化为0.

 

17. std::array

std::array跟数组没有多大的区别。也就是不可增容的数组

std::array相对于数组没有太大的区别,增加了迭代器等函数

#include <iostream>
#include <array>
​
int main()
{
    std::array<int, 4> array = { 1, 2 , 3, 4 };
    for (auto e : array)
    {
        std::cout << e <<std::endl;
    }
​
    int array_size = sizeof(array);
    std::cout << array_size << std::endl;
    return 0;
}

 

18. std::function封装可执行对象

std::bind和std::function是从boost中移植进来的C++新标准,这两个语法使得封装可执行对象变得简单而易用。

std::bind和std::function也可以结合lambda表达式一起使用

std::function < int (int, int) >表示std::function封装的可执行对象返回值和两个参数均是int类型

【注意】:对于使用std::function来封装一个执行体对象的时候,必须要包含头文件

#include <functional>

可以封装的执行体:普通函数,Lamda表达式,仿函数,函数指针以及类成员函数和类静态成员函数

std::function对C++中可执行实体进行封装,形成一个新的可调用的std::function对象

原型:

template <class R, class ... Args>
class function<R(Args...)>
​
R是返回值类型,Args是函数的参数类型

19. std::bind

将要调用的对象和调用对象的参数进行绑定,返回新的可调用对象(std::function类型,参数列表可能改变),返回新的std::function可调用对象的参数列表根据bind函数实参中的std::placeholders::_x从小到大对应的参数确定

#include <iostream>
using namespace std;
class A
{
    public:
    void fun_3(int k,int m)
    {
        cout<<k<<" "<<m<<endl;
    }
};
​
void fun(int x,int y,int z)
{
    cout<<x<<"  "<<y<<"  "<<z<<endl;
}
​
void fun_2(int &a,int &b)
{
    a++;
    b++;
    cout<<a<<"  "<<b<<endl;
}
​
int main(int argc, const char * argv[])
{
    auto f1 = std::bind(fun,1,2,3); //表示绑定函数 fun 的第一,二,三个参数值为: 1 2 3
    f1(); //print:1  2  3
​
    auto f2 = std::bind(fun, placeholders::_1,placeholders::_2,3);
    //表示绑定函数 fun 的第三个参数为 3,而fun 的第一,二个参数分别有调用 f2 的第一,二个参数指定
    f2(1,2);//print:1  2  3
​
    auto f3 = std::bind(fun,placeholders::_2,placeholders::_1,3);
    //表示绑定函数 fun 的第三个参数为 3,而fun 的第一,二个参数分别有调用 f3 的第二,一个参数指定
    //注意: f2  和  f3 的区别。
    f3(1,2);//print:2  1  3
​
​
    int n = 2;
    int m = 3;
​
    auto f4 = std::bind(fun_2, n,placeholders::_1);
    f4(m); //print:3  4
​
    cout<<m<<endl;//print:4  说明:bind对于不事先绑定的参数,通过std::placeholders传递的参数是通过引用传递的
    cout<<n<<endl;//print:2  说明:bind对于预先绑定的函数参数是通过值传递的
​
​
    A a;
    auto f5 = std::bind(&A::fun_3, a,placeholders::_1,placeholders::_2);
    f5(10,20);//print:10 20
​
    std::function<void(int,int)> fc = std::bind(&A::fun_3, a,std::placeholders::_1,std::placeholders::_2);
    fc(10,20);//print:10 20
​
    return 0;
}

 

 

20. lamda表达式

lamda就是一个匿名函数

要使用lamda就是要用一个函数指针来接收这个匿名函数的地址,然后再次使用这个函数指针来调用lamda表达式

【注意】:对于lamda的接收可以

  • 使用std::function来封装一个执行体对象

  • 使用auto语句在编译的时候,自动生成一个符合该类型的变量

int main()
{
    auto add= [](int a, int b)->int{
        return a + b;
    };
    int ret = add(1,2);
    std::cout << "ret:" << ret << std::endl;
    return 0;
}
#include <iostream>
#include <functional>
​
std::function<void()> myfunc;
​
class Basic
{
    private:
    int _num;
    char _c;
​
    public:
    Basic(): _num(10), _c('c')
    {}
​
    void Set()
    {
        //可以修改该类的成员变量
        myfunc = [this]()->void{
            _num = 11;
            _c = 'b';
        };
        //必须调用这个函数
        myfunc();
    }
​
    void Show()
    {
        std::cout << "_num: " << _num << "\n_c: " << _c << std::endl;
    }
};
​
int main()
{
​
    Basic b;
    b.Show();
    b.Set();
    b.Show();
    //std::string s = "hello world!";
    //std::function<void ()> myfunc;
    //不能捕获所在函数中的变量
    //myfunc = []()->void{
    //  //无法捕获到变量s
    //  std::cout << s << std::endl;
    //  //可以输出
    //  std::cout << "1" << std::endl;
    //};
​
    //以值传递的方式捕获
    //myfunc = [=]()->void{
    //  std::cout << s << std::endl;
    //  //s += "asd"; //出错
    //};
    //std::cout << s << std::endl;
​
​
    //以引用的方式捕获
    //myfunc = [&]()->void{
    //  std::cout << s << std::endl;
    //  //改变了外边的变量
    //  s += "abc";
    //  std::cout << s << std::endl;
    //};
    //myfunc();
    //std::cout << s << std::endl;
    return 0;
}

[]:中括号用于控制在main函数中,lamda表达式之前的变量在lambda表达式中的访问形式

  • []:lamda表达式中不能够使用所在函数(main)中的变量

  • [=]:值捕获,lamda表达式中可以以拷贝的方式访问到函数中变量的值

  • [&]:引用捕获,lamda表达式使用所在函数中的变量都是用引用的方式

  • [this]:捕获this指针的成员。也就是lamda表达式必须是在一个类中

(int a, int b):函数的形参

->int:lamda表达式的返回值定义

{}:大括号内为lamda表达式的函数体

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值