对于每一种语言来说,最主要的不过是语法和语义,而构成这两者的则是关键词和各种操作符。以上是我瞎掰的,但是关键词绝对是一门语言的最基本和最重要的元素,如果连关键词都不知道,又如何去掌握一门语言呢。所以说,我们起航第二步当然是准备好粮食,来一个持久的旅行。
大话就不扯了,开始进入主题。C++ 11标准为我们带来了10个新的关键词,分别为:
- alignas
- alignof
- auto 重新定义
- constexpr
- default
- delete 重新定义
- export 弃用,未来可能留作他用
- final
- noexcept
- nullptr
- override
- static_assert
- thread_local
- using 重新定义
写这篇文章的此时,其实我也是还有几个关键词不知是干什么用的,所以大家共同学习,共同进步。如果有不对的地方,还望大家给予指点修正。接下来就是对每个关键词展开讨论介绍了。
1. alignas
看到align自然就知道这个关键词是跟对齐有关的,意思是对齐描述符(alignment-specifier),先来看一个C++的经典例子:
struct A
{
char mA;
char mB;
char mC;
char mD;
int mE;
};
struct B
{
char mA;
char mB;
int mC;
char mD;
char mE;
};
编译器默认是4字节对齐,而B这个结构体没有对字节进行对齐,会导致有4个字节空间的浪费。如下图:
这个就不进行赘述了,那关于alignas就是我们的对齐方式的设置了,它可以接受一个常量作为参数,也可以接受一个类型作为参数,例如:
// 以常量作为参数 alignas(32) int mA; // 以类型作为参数 alignas(int) int mB;
关于常量作为参数,其单位是字节,即如果对mA求sizeof,大小为32,这个是第一个要注意的地方。第二个注意的地方是,如果以常量作为参数,其他大小应该为2的自然数幂次方作为对齐值。
2. alignof
alignof和sizeof类似,是求一个自定义类型或者内置类型或者变量的对齐值,类型为std::size_t,例如:
cout << alignof(A) << endl;
cout << alignof(B) << endl;
A,B暂用上面的结构体,具体值是多少,默认值4嘛。需要注意的是,返回的是字节长度,而不是位长度。
3. auto
这个是C++ 11非常有意思的一个东西,预计未来会成为我们得力的助手,别的不多说,先来看几个例子:
第一个:
std::vector<int> intVec{1, 2, 3, 4, 5, 6};
for (auto i : intVec) {
cout << i << endl;
}
第二个:
auto str = "Hello world."; cout << str << endl;
第三个:
还需要我费口舌去说明什么吗?在这里我看到了动态类型的影子,再也不用定义长长的迭代器了,再也不用关心类型了,再也不用关心返回值了。够不够犀利?!好处是很多,自然也会有所限制,要注意以下几点:int Func(); // 省略 auto ret = Func();
1>>constexpr int GetValue() { return 1;}
但是对于函数来说,却有诸多的限制(主要原因是要在编译期间完成函数的解析):
1> 只有单一的一条return语句,即此函数内只能有一行代码,如果写成int>
2> 函数必须有返回值,不能为void。
3>>constexpr int INT_VAL = 1;
其用法与const类似,不再过多解释。(TODO: 应用于指针时需要进行测试)
最后,常量构造函数,为什么会有这个概念呢?主要是跟上面常量表达式值有关系的,假设用户自定义类型如下:
class ConstTest { public: ConstTest(int pA) : mA(pA) {} private: int mA; };
现在想把这个类作为常量表达式值该怎么办呢?这个只要这么写就可以了:
class ConstTest { public: constexpr ConstTest(int pA) : mA(pA) {} private: int mA; }; constexpr ConstTest CLASS_VAL = {1};
这下大家应该明白了吧。不过要注意两点,第一,构造函数内部必须为空,即全部参数通过初始化列表构造;第二,初始化列表只能由常量表达式赋值,不能通过自定义的函数等形式来赋值,例如上面的例子:constexpr ConstTest(int>class TestA{public: TestA(int pA) : mA(pA) {} void Func() { cout << "TestA - mA: " << mA << endl; }private: int mA;};class TestB{public: void Func() { mA->Func(); cout << "TestB - mB: " << mB << endl; }private: TestA *mA = new TestA(1); int mB = 2;};int main(){ TestB b; b.Func(); return 0;}
写出上面的代码,我已经泪流满面了,TestB完全不需要构造函数了有木有?!不信的自己可以尝试一下。接下来就要说一下default这个关键词的作用了。大家都知道,C++会默认给我们每个类实现一个构造函数,但是我们自己定义了一个有参的构造函数(即不是默认构造)时,默认构造函数就不能被调用了,必须重新实现才行,例如上面提到的TestA,就不能定义一个TestA>class TestA{public: TestA() = default; TestA(int pA) : mA(pA) {} void Func() { cout << "TestA - mA: " << mA << endl; }private: int mA;};
很简单,就是默认构造函数后面加上一个=>class TestA{public: TestA(); TestA(int pA) : mA(pA) {} void Func() { cout << "TestA - mA: " << mA << endl; }private: int mA;};TestA::TestA() = default;
同样也很简单。另外C++ 11中有5种默认缺省函数,这里就先不展开讨论了,以后单独拿出一节来分析。
5.>class TestA{public: TestA(); TestA(int pA) : mA(pA) {} TestA(const TestA &pTestA) = delete; void Func() { cout << "TestA - mA: " << mA << endl; }private: int mA;};int main(){ TestA a(1); TestA b = a; return 0;}
这样可行么?必然会编译失败,编译器告诉我们以下信息:
error: use of deleted function 'TestA::TestA(const TestA&)'
这下明白这个关键字的作用了吧,尝试才是我们学习最好的老师。我想这个关键字的主要作用还是防止友元和子类对类的某些函数的访问,毕竟写成private也不一定就是安全的,这样直接delete掉就绝对不能使用了,非常痛快!(2013/12/24,今天先到这吧,洗洗睡了,明天继续~)
6.>class TestA{public: ~TestA() {} virtual void FuncA() = 0; virtual void FuncB() = 0;};class TestB : public TestA{public: void FuncA() final { cout << "TestB FuncA." << endl; } void FuncB() override { cout << "TestB FuncB." << endl; }};class TestC : public TestB{public: void FuncA() override { cout << "TestC FuncA." << endl; } void FuncB(int pA) override { cout << "TestC FuncB." << endl; }}; 已经看到final和override的应用了吧?没错,这两个关键词主要应用场景是在继承体系中。上面的代码编译后会提示以下错误:
error: virtual function 'virtual void TestC::FuncA()' error: overriding final function 'virtual void TestB::FuncA()' error: 'void TestC::FuncB(int)' marked override, but does not override那就先来介绍一下这两个关键词的作用吧,上面的错误自然迎刃而解。
第一,C++中子类继承基类的方法时,是不需要再添加virtual的声明的,即TestB和TestC会自动生成虚函数表,这个作为前提。
第二,final的作用就是终止某一函数的继承,即TestB终止了FuncA的继承,子类再实现此方法就会报错。这样做的原因很容易理解,我们在继承体系中,某一方法的实现已经固定,不希望子类去重新实现打破管理就可以使用final,使得继承更加安全。
第三,override的使用其实也是提高了安全性,还是以上面的例子来说,每次继承时都添加override,那样在继承了很多层次后,我们仍知道某一函数是继承过来的;并且我们如果写错了这个函数,编译器是不会通过的。例如TestC中FuncB的实现里,添加了一个参数,导致编译失败。从各个方面来说,安全性都得到了极大的提升。
这两个关键词预测会成为未来C++很关键的两大战将,我们应该早日掌握其应用,发挥其战斗力。不才只是在这列举一两个小例子,应用的场景还要靠大家去推敲。
8. noexcept
异常,不得不说是C++中一个很纠结人心的东西。只是使用就会导致效率大降,用的不好则会各种程序崩溃,至于原因在这就不过多介绍了,因为光是写异常都可以写一本书了。下面开始介绍一下noexcept这个关键词,老样子,先来代码看一下:
void FuncA() noexcept { cout << "FuncA." << endl; } void FuncB() noexcept(true) { cout << "FuncB." << endl; }
这两个就是最简单的两个场景了,第一个是修饰符,第二个是操作符,这里我就统一叫做关键词了,这两种用法,本质上相当于throw(),即不抛出异常。其中noexcept(bool)可以接收一个bool值,true表示不抛出异常,false表示有可能抛出异常。并且这里bool值还可以通过一个函数来替换,例如noexcept(func()),另外noexcept也可以用于模版中。其他就不再过多介绍了,因为我认为异常机制在C++中并不是一个很好的机制,具体在C++ 11中有没有新的更改,需要以后再进行探讨了。
9. nullptr
这个其实很直白,NULL的新的表现方式,例如:
int *pInt = nullptr;就是相当于一个空的int指针而已,但是要注意,nullptr实际上是一种类型,而不是一个值(0)。通过源码(cstddef)中,nullptr有以下定义:
typedef decltype(nullptr) nullptr_t;即nullptr_t才是真正的类型,下面我们来测试一下nullptr_t:
int main() { nullptr_t null; cout << &null << endl; cout << (nullptr==null ? 1 : 0) << endl; return 0; }输出结果为:
可以看到,nullptr_t默认值就是nullptr,并且地址是跟编译器相关的,所以我们使用的过程中要注意不要对其做取地址操作。(2013/12/25,洗洗睡了~)
CSDN太让人失望了,一不小心手贱按了下ctrl+z,结果文章全乱掉了,伤心啊。。。。周末再重写吧,不爽。。。。