const修饰变量关于const最常见的一个面试题是这样的:char *const和const char*有什么区别,大家都知道const修饰符代表的是常量,即const修饰的变量一旦被初始化是不能被更改的,这两个类型一个代表的是指针不可变,一个代表指针指向内容不可变,但具体哪个对应哪个,很多人一直搞不清楚。有这样一个规律,const修饰的是它前面所有的数据类型,如果const在最前面,那么把它和它后面第一个数据类行交换.比如上面的const char*交换之后就是char const *,这样一来就很清楚了,char *const p中的const修饰的是char *(注意,我们这里把char和*都算作一种类型,这时候const修饰的是char和*的组合,也就是字符串指针),是一个指针类型,所以这时候指针p是不能变的,比如下面这段代码就会报错复制代码 代码如下:char str1[]="str1";  char str2[]="str2";  char *const p = str1;  p = str2;  这时候p是一个指针常量,它是不能指向别的地方的,但是它本身指向的内容是可以变的,比如下面的操作就是允许的复制代码 代码如下:char str1[]="str1";  char *const p = str1;  p[0] = 'X';  printf("%s", str1);  这时候str1的值就变成了"Xtr1"我们再来看const char *p,根据前面提到的规律,将const和它后面一个类型交换变成char const *p(其实这种写法也是允许的,只是人们习惯将const写在最前面),这时候const修饰的是char,也就是说p指向的字符内容是不能变的。将上面两个例子的char *const p全部改成const char *p,则结果正好相反,第一个可以编译通过,第二个会报错。其它时候就很好区分了,比如const int ,const string等等,总之,const修饰的是什么类型,这个类型的变量就不能被改变。const修饰函数先来看这样一个函数复制代码 代码如下:const char * func(const char *str) const;这样的函数比较夸张,有三个const,我们从左到右来一一说明:1、第一个const修饰的是返回值,前面已经说过,这里的const修饰的是char,也就是说返回值的内容是不能被更改的2、第二个const和第一个是一样的,这种用的比较多,它作为函数参数,表示的是这个参数在函数体内是不能被改动的(被传进来的实参并不要求是const类型),这样做是为了防止函数对实参做一些意外的操作,你试想下,当你调用一个函数时,你传进去一个变量是"hello world!",调完函数之后变成了"fuck the world!",这实在是不可忍的,所以我们在设计函数的时候,如果传进来的参数只作为读取使用,最好是将参数设成const类型。很多公司在面试让写代码的时候都会看中这个细节,你注意了这个细节不一定说明你牛逼,但你若没注意那肯定是会减分的。3、再来说第三个const,按照我们最开始说的规律,const修饰的是它前面的所有数据类型,这里它前面的所有数据类型组合起来就是一个函数,这种类型一般出现在类成员函数里,当然,这里并不是说这个函数是不能变的,它代表的时这个函数不能改变类的成员变量,不管是public的还是private的我们下面举例主要说明第三种情况,来看这样一个简单的程序复制代码 代码如下:#include<stdio.h>    class A  {  public:      A() : x(0), y(0){}      void func(const int p)      {          x = p;          y = p;      }      int getY()      {          return y;      }      int x;  private:      int y;  };    int main(int argc, char* argv[])  {      A a;      printf("x:%d y:%d\n", a.x, a.getY());      a.func(2);      printf("x:%d y:%d\n", a.x, a.getY());      return 0;  }  这段代码是可以直接编译过的,运行结果是复制代码 代码如下:x:0 y:0  x:2 y:2  我们稍作修改,将void func(const int p)改成void func(const int p) const再编译,就会直接报错,报错的两行代码是复制代码 代码如下:x = p;  y = p; 也就是说const类型的函数试图去修改类的成员变量是非法的,但是有一种情况例外,我们再在上面修改的基础上做一点修改,将int x改成mutable int x,将int y改成mutable int y,这时候程序又可以正常运行了,也就是说,如果成员变量是mutable类型的,它可以在任何场景下被修改。

前言在C++11新标准中,语言本身和标准库都增加了很多新内容,本文只涉及了一些皮毛。不过我相信这些新特性当中有一些,应该成为所有C++开发者的常规装备。本文主要介绍了C++11中lambda、std::function和std:bind,下面来一起看看详细的介绍吧。lambda 表达式C++11中新增了lambda 表达式这一语言特性。lambda表达式可以让我们快速和便捷的创建一个”函数”。下面是lambda表达式的语法:[ capture-list ] { body }[ capture-list ] ( params ) { body }[ capture-list ] ( params ) -> ret { body }[ capture-list ] ( params ) mutable exception attribute -> ret { body }这其中: - capture-list 是需要捕获的变量列表,用逗号分隔。其详细说明见下文。 - params 是lambda表达式需要的参数列表,写法和函数参数一样,不过这里不支持默认参数。 - ret 指明了lambda表达式的返回值。通过return语句,如果编译器能够推断出返回值的类型。或者表达式没有返回值,“-> ret”可以省略。 - body 函数体。 - mutable 当捕获列表是以复制(见下文)的形式捕获时,默认这些复制的值是const的,除非指定了mutable。 - exception 提供了异常的说明。 - attribute 对于attribute的描述可以参见这里:http://en.cppreference.com/w/cpp/language/attributes,这里不多说明。下面,我们通过经典的Hello World示例来看一下lambda表达式:auto lambda1 = [] {std::cout << "Hello, World!\n";};lambda1();这个lambda表达式将打印出字符串“Hello, World!”。同时,我们将这个表达式赋值给“lambda1”这个变量,然后像调用函数一样,调用这个lambda表达式。使用lambda表达式,可以让我们省却定义函数的麻烦,以inline的方式写出代码,这样的代码通常更简洁。并且,由于阅读代码时不用寻找函数定义,这样的代码也更易读。下面,我们来看另外一个例子。这个例子的需求是:分两次,打印出一个vector集合中,所有:1. 模 5 = 02. 大于 20的数字。现假设已有这个集合的定义如下:vector<int> numbers { 1, 2, 3, 4, 5, 10, 15, 20, 25, 35, 45, 50 };我们最先想到的方法自然是定义两个函数,分别按照上面的要求打印出需要的数字,它们的定义如下:void printNumber1(vector<int>& numbers) { for (const int& i : numbers) { if (i % 5 == 0) { cout<<i<<endl; } }}void printNumber1(vector<int>& numbers) { for (const int& i : numbers) { if (i % 5 == 0) { cout<<i<<endl; } }}然后,我们在需要的地方,调用它们:printNumber1(numbers);printNumber2(numbers);这里逻辑上并没有问题,但是:1. 这里我们必须先定义这个函数,才能使用。而这样的函数,可能实际上我们只会使用一次。2. 当工程大到一定程度,我们可能不记得每个函数的实现(所以函数命名很重要,原谅我这里给函数起了很含糊的名字,你在实际上工程中,请不要这样做),为了知道每个函数的实现,我们不得不查看函数的定义,这无疑给代码的阅读造成了一定的麻烦。下面,我们来看看使用lambda表达式如何改善上面说的问题。使用lambda表达式,我们可以这样写:for_each(numbers.begin(), numbers.end(), [] (int i) { if(i % 5 == 0) { cout<<i<<endl; }});for_each(numbers.begin(), numbers.end(), [] (int i) { if(i > 20) { cout<<i<<endl; }});这里,我们不用单独定义函数,直接以inline的方式解决了问题。并且,这段代码一气呵成,你很直观的看到了执行的逻辑。下面,我们再详细看一下lambda表达式中的捕获列表的语法,它可能是以下几种情况中的一种: [] 不捕获任何变量 [&] 以引用的方式捕获所有变量 [=] 以复制的方式捕获所有变量 [=, &foo] 以引用的方式捕获foo变量,但是以复制的方式捕获其他变量 [bar] 以复制的方式捕获bar变量,不再捕获任何其他变量 [this] 捕获this指针 下面,我们再以一个例子说明捕获列表的用法。这里,我们的需求是:打印出一个vector<int>的所有数字之和同样的,我们先以函数的方式来解决这个问题,这个函数的定义可以是这样的:void printSum(vector<int>& numbers) { int sum = 0; for (const int& i : numbers) { sum += i; } cout<<sum<<endl;}然后,我们在需要的地方调用这个函数:vector<int> numbers { 1, 2, 3, 4, 5, 10, 15, 20, 25, 35, 45, 50 };printSum (numbers);而假设我们用lambda表达式来写,这样写就可以了:vector<int> numbers { 1, 2, 3, 4, 5, 10, 15, 20, 25, 35, 45, 50 };int sum = 0;std::for_each(numbers.begin(), numbers.end(), [&sum] (const int& i) { sum += i;});cout<<sum<<endl;这里,我们用 [&sum]以引用的形式捕获了sum这个变量,并且在lambda表达式中修改了这个变量。这样写,是不是比定义函数的方式简洁了很多?对于这种,能够捕获其定义时上下文变量的函数,我们称之为“闭包”,下文还将提到。std::function上文中,对于分两次,打印出一个vector集合中,所有:1. 模 5 = 02. 大于 20的数字。这个需求,我们的实现其实还不够好。回头看一下printNumber1和printNumber2这两个函数,这两个函数大部分都是重复的:它们都需要遍历集合,都需要做if判断,然后打印出结果。实际上,我们在项目中经常遇到这个的问题:两(多)个函数,有大部分的代码都是一样的,其中只有一两行代码有不一样的地方。其实,我们可以对这个不一样的地方,再做一个抽象,把它们共通起来。具体到这个例子就是:无论是“模 5 = 0”还是“大于 20”都是满足“某种条件”。而很自然的会想到,我们是否可以通过一个类似这样的函数来做这个判断:bool func(int i)然后实现两个函数,通过函数指针的形式来完成判断就好了。但是,我们马上又意识到,这两个函数会很小,并且也是只会用一遍而已,定义一个函数又太“浪费”了。 很自然的,我们就会想lambda。但是,lambda似乎没法转成函数指针。。。C++11中,提供了一个通用的描述方法,就是std::function。 std::function可以hold住任何可以通过“()”来调用的对象,包括: 普通函数 成员函数 lambda std::bind(见下文)后的结果 std::function的语法是这样:template <class Ret, class... Args> class function<Ret(Args...)>; 例如:function<bool (int)> filter 就表达了我们前面需要的那个函数:这个函数接受一个int值作为参数,同时返回一个bool作为判断的结果。但同时,我们可以用lambda表达式直接传递进去。因此,上面的代码可以改写成这样:void printNumber(vector<int>& number, function<bool (int)> filter) { for (const int& i : number) { if (filter(i)) { cout<<i<<endl; } }}然后在需要的地方,这样调用即可:printNumber(numbers, [] (int i){ return i % 5 == 0;});printNumber(numbers, [] (int i){ return i > 20;});这种做法,是不是又简洁了不少?闭包前面提到了“闭包”这个词,这里我们来聊一下闭包。下面是维基百度对于闭包的定义:在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。 这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。简单来说:闭包可以记忆住创建它时候的那些变量。下面,我们再通过一个例子来说明。现在,假设我们的需求是:获取一个集合中最小和最大值,并在稍后的时候(可能是另外一个函数中)打印它们。 这里,我们常规的做法通常是:通过一个函数获取集合的最大,最小值,然后保存住,最后在需要的时候访问这两个值,然后打印它们。这样做就会需要解决:如果保存和传递最大,最小这两个值。但实际上,这里我们可以考虑用闭包来实现这个功能,让闭包把最大,最小两个值捕获下来,然后在需要的地方调用就可以了。请看一下下面这段代码:void getMinMax(vector<int>& number, function<void ()>& printer) { int min = number.front(); int max = number.front(); for (int i : number) { if (i < min) { min = i; } if (i > max) { max = i; } } printer = [=] () { cout << "min:" <<min<< endl; cout << "max:" << max << endl; };}这里,我们通过function<void ()>& printer(如果你看不懂function,请看上文)传递出这个闭包。 然后,在需要的地方,这样即可:function<void()> printer;getMinMax(numbers, printer);......printer();这里的printer其实是我们前面从getMinMax函数出传出的闭包,这个闭包捕获了min和max。我们直接传递这个闭包给需要的地方使用,而不用传递裸的两个数值,是不是优雅的不少?std::bind下面,我们再改进一下需求,假设我们要 打印出vector<int>中,20<x<40范围内的值 ,该怎么办?毕竟,bool isBetween(int i, int min, int max) 这个函数可没法对应上function<bool (int)> filter 啊!参数数量就不一样嘛。这个时候,我们可以用 std::bind 。std::bind的语法是这样的:template <class Fn, class... Args> bind (Fn&& fn, Args&&... args);template <class Ret, class Fn, class... Args> bind (Fn&& fn, Args&&... args);std::bind可以将调用函数时的部分参数先指定好,留下一部分在真正调用的时候确定。(当然,你也可以直接指定全部参数,在调用时不再指定。)这里,isBetween中,最小,最大值其实我们是确定了的,即:20和40。而不确定的,其实是真正待判断的数字本身,那么我们就可以这么做:std::bind(isBetween, placeholders::_1, 20, 40);placeholders::_1 的意思是,这里是一个占位符,在调用的时候,将实际传递的第一个参数放到这里。占位符的数量可以是任意多的,像这样:std::placeholders::_1, std::placeholders::_2, …, std::placeholders::_N。于是乎,对于 打印出vector<int>中,20<x<40范围内的值 这个需求,我们在不修改printNumber函数的基础上,通过定义一个isBetween函数:bool isBetween( int i, int min, int max) { return i >= min && i <= max;}然后,再这样就搞定了:function<bool(int)> filter = std::bind(isBetween, placeholders::_1, 20, 40);printNumber(numbers, filter);当然,你甚至可以直接把这里的两行写成一行。如果你不明白这段代码,请再看一下printNumber函数的定义:void printNumber(vector<int>& number, function<bool (int)> filter) { for (const int& i : number) { if (filter(i)) { cout<<i<<endl; } }}这里其实调用了filter(i)这个函数对象,而这个函数对象只接受一个int值作为参数,然后返回一个bool值。function<bool(int)> filter = std::bind(isBetween, placeholders::_1, 20, 40); 绑定之后,只缺一个int型参数,所以正好对应得上。如果不过瘾,我们再来看一个bind的例子。我们常常需要在程序中,调用一些用户传过来的回调函数。而在回调函数中,用户常常会需要记录一些状态,于是常常希望通过一个对象的成员函数传给过来作为回调函数。但是在C++中,这样做是很麻烦的一个事情。因为,回调函数的类型我们很难定义。 但是,结合std::function和std::bind,一切变得容易多了。 结合前面的例子,现在就假设我们的回调函数是需要打印集合中的最大,最小值。这里假设我们是通过一个类来记录和打印值的,这个类的定义是这样的:class Printer {private: int min, max;public: Printer(int x, int y) { min = x; max = y; } void print() { cout << "min:" << min << endl; cout << "max:" << max << endl; }};由于回调函数不需要参数,因此使用回调函数的代码是这样的:void usingCallback(function<void ()> print) { print();}然后,我们可以通过下面的方法来调用print函数Printer printer = Printer(10, 50);function<void ()> print = bind(&Printer::print, printer);usingCallback(print);成员函数其实是类中的方法绑定到一个对象上,然后执行调用。这里的代码很直观的表达了这个关系。lambda表达式是如何实现的lambda表达式是如何实现的呢?其实是编译器为我们了创建了一个类,这个类重载了(),让我们可以像调用函数一样使用。所以,你写的lambda表达式和真正的实现,是这个样子的: 而对于捕获变量的lambda表达式来说,编译器在创建类的时候,通过成员函数的形式保存了需要捕获的变量,所以看起来是这个样子:似乎也没有什么神奇的地方。但正是由于编译器帮我们实现了细节,使我们的代码变得优雅和简洁了许多。参考资料http://www.cprogramming.com/c++11/c++11-lambda-closures.htmlhttp://www.drdobbs.com/cpp/lambdas-in-c11/240168241https://en.wikipedia.org/wiki/Closure_(computer_programming)http://www.jellythink.com/archives/771http://en.cppreference.com/w/cpp/utility/functional/functionhttps://en.wikipedia.org/wiki/First-class_functionhttps://blog.feabhas.com/2014/03/demystifying-c-lambdas/总结以上就是这篇文章的全部内容了,希望本文的内容对大家学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。谢谢大家对脚本之家的支持。

最大的区别就是C风格的字符串是静态的,不可以动态变化,使用极为麻烦。而C++的std::string类型动态管理,非常方便。C风格字符串和char数组是不一样的,看下面两种定义:char carr1 = {'a', 'b', 'c'};char carr2 = {'a', 'b', 'c', '\0'};看上面,carr2可以说成是C风格字符串,carr1就不是C风格字符串,C风格字符串必须要以'\0'结尾的。string类是标准库的类,并不是内置类型,标准库就像是我们自己定义的类差不多的,string类型对象没有标配'\0'结尾的。如果需要用string类给C风格字符串赋值的话,后面是需要添加'\0'的。

我们在程序设计中,时时刻刻都用到变量的定义和变量的声明,可有些时候我们对这个概念不是很清楚,知道它是怎么用,但却不知是怎么一会事,下面我就简单的把他们的区别介绍如下:变量的声明有两种情况:(1) 一种是需要建立存储空间的(定义、声明)。例如:int a在声明的时候就已经建立了存储空间。 (2) 另一种是不需要建立存储空间的(声明)。例如:extern int a其中变量a是在别的文件中定义的。前者是"定义性声明(defining declaration)"或者称为"定义(definition)",而后者是"引用性声明(referncing declaration)"。从广义的角度来讲声明中包含着定义,但是并非所有的声明都是定义,例如:int a它既是声明,同时又是定义。然而对于extern a来讲它只是声明不是定义。一般的情况下我们常常这样叙述,把建立空间的声明称之为"定义",而把不需要建立存储空间称之为"声明"。很明显我们在这里指的声明是范围比较窄的,也就是说非定义性质的声明。例如:在主函数中 复制代码 代码如下:int main(){extern int A; //这是个声明而不是定义,声明A是一个已经定义了的外部变量  //注意:声明外部变量时可以把变量类型去掉如:extern A;dosth();  //执行函数}int A;//是定义,定义了A为整型的外部变量(全局变量) 外部变量(全局变量)的"定义"与外部变量的"声明"是不相同的,外部变量的定义只能有一次,它的位置是在所有函数之外,而同一个文件中的外部变量声明可以是多次的,它可以在函数之内(哪个函数要用就在那个函数中声明)也可以在函数之外(在外部变量的定义点之前)。系统会根据外部变量的定义(而不是根据外部变量的声明)分配存储空间的。对于外部变量来讲,初始化只能是在"定义"中进行,而不是在"声明"中。所谓的"声明",其作用,是声明该变量是一个已在后面定义过的外部变量,仅仅是在为了"提前"引用该变量而作的"声明"而已。extern只作声明,不作定义。 用static来声明一个变量的作用有二:(1) 对于局部变量用static声明,则是为该变量分配的空间在整个程序的执行期内都始终存在(2) 外部变量用static来声明,

1.C++的结构体变量在声明的时候可以省略struct,在c中这样是不可以的,例子如下#include<iostream>#include<string>using namespace std;struct test{ int num; string name;};int main(void){ test t; t.num=1; t.name="jack"; cout<<t.num<<" "<<t.name<<endl; }2.c++的结构体声明可以声明在main()函数中,也可以在main()函数之前,在之前的话,整个程序都可以调用,这也是最常用的方式;而在内部的话,只能在函数内使用,也就是说在声明的函数内可以使用,类似于局部变量的概念。

分类:36salon手机版

时间:2016-05-07 15:15:49