一,C++11引入列表初始化来禁止缩窄(长度数据类型强转小长度类型引起的数据丢失)。可将用于初始化的变量或值放到大括号{}内:
int largeNum = 500000; short anotherNum{largeNum }; // error! Amend types int anotherNum{largeNum}; // OK! float someFloat{largeNum}; // error! An int may be narrowed float someFloat{500000}; //OK! 500000 can be accomodated
二,C++11引入了固定宽度的整形,能够以位为单位指定整数的宽度。这些类型为int8_t和uint8_t,分别用于存储8位的有符号和无符号整数。还可以使用16位、32位和64位整型,分别是int16_t、uint16_t、int32_t、uint32_t、int64_t和uint_64_t。要使用这些类型,比如包含头文件<cstdint>。
三,使用八进制或二进制字面常量赋值:
int someNumber = 012; // 八进制12赋值到十进制整型变量; int someNumber = 0b1010; // 二进制1010赋值都十进制整型变量;
四,使用constexpr定义常量表达式:
通过关键字constexpr可以让常量声明像函数:
constexpr double GetPI() {return 22.0/7;}
在一个常量表达式中,可以使用另外一个常量表达式:
constexpr double TwicePI() {return 2*GetPI();}
常量表达式看起来像函数,但在编译器和应用程序来看,它们提供了优化可能性。只要编译器能够从常量表达式中计算出常量,就可以在语句和表达式中对可使用常量的地方使用它。
常量表达式必须包含简单的实现并返回简单的类型。如整型、双精度浮点数等。在C++14中,常量表达式可包含决策结构,如if和switch语句。
使用constexpr并不一定能够保证会进行编译阶段优化,比如,使用常量表达式来计算用户输入数字的两倍,由于编译器无法计算这种表达式的结果,则可能会忽略constexpr,进而将常量表达式视为常规函数进行编译。
可以使用constexpr来将类的构造函数定义为常量表达式,可能有助于提高性能:
class Sample { const char* someString; public: constexpr Sample(const char* input) :someString(input) { // constructor code } };
五,字符串结束字符'\0',它告诉编译器,字符串到此结束。这种C风格字符串是特殊的字符数组,因为总是在最后一个字符加上空字符'\0'。而在代码中使用字符串字面量时,编译器将负责在它后面添加'\0'。
在数组中间插入'\0'并不会改变数组长度,而只会导致将该数组作为字符串处理时,将到这个位置结束。
'\0'看起来像两个字符,但反斜杠是编译器能够理解的特殊转移编码,\0标识空,即它让编译插入空字符或零。不能将其写成'0',因为它标识字符0,其ASCII编码为48。
六,inline关键字
在C/C++中,为了解决一些频繁调用小函数而大量消耗栈空间(栈内存)的问题,特别引入了inline修饰符,表示内联函数。栈空间就是指放置程序的局部数据(函数内数据)的空间。在系统下,栈空间是有限的,假如频繁大量使用就会造成栈空间不足而导致程序出错的问题。
#include <stdio.h> //函数定义为inline即:内联函数 inline char* dbtest(int a) { return (i % 2 > 0) ? "奇" : "偶"; } int main() { int i = 0; for (i=1; i < 100; i++) { printf("i:%d 奇偶性:%s /n", i, dbtest(i)); } }
上面的例子就是标准的内联函数用法,使用inline修饰带来的好处,我们表面上看不出来。其实,在内部的工作就是在每个for循环调用dbtest(i)的地方都换成(i % 2 > 0) ? "奇" : "偶",这样就避免了频繁调用函数对栈内存重复开辟所带来的消耗。
1)inline使用限制:只适合函数内代码简单的函数使用,不能包含复杂的结构控制语句while/switch等,并且内联函数本身,不能是直接递归函数。
2)inline仅是对编译器的建议,最后能否真正内联,看编译器的意思;
3)linline函数的定义放在头文件中,因为内联函数要在调用点展开,编译器必须随处可见内联函数的定义。
4)类中的成员函数与inline
定义在类中的成员函数缺省是内联的,即在类定义时就给出函数定义。否则就不是内联的。
class A { public:void Foo(int x, int y) { } // 自动地成为内联函数 }
如果成员函数的定义体不在类声明中,则需要单独声明:
// 头文件 class A { public: void Foo(int x, int y); }
// 定义文件 inline void A::Foo(int x, int y){}
注意inline必须和定义体在一起才能声明为内联函数:
inline void Foo(int x, int y); // inline 仅与函数声明放在一起,不是内联函数 void Foo(int x, int y){}
void Foo(int x, int y); inline void Foo(int x, int y) {} // inline 与函数定义体放在一起,是内联函数
5)慎用inline:内联能提高函数的执行效率,但是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率,同时消耗更多的内存空间。以下情况不宜使用内联:
a)如果函数体内的代码较长,使用内联将导致内存消耗代价较高;
b)如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。
6)类的构造函数和析构函数容易让人误解呈使用内联函数更有效。要当心构造函数和析构函数可能会隐藏一些行为,如“偷偷地”执行基类或成员对象的构造函数和析构函数,所以不要随便将构造函数和析构函数的定义体放在类声明中。
七,lambda函数:
[optional parameters](parameter list) {statements;}
八,组块分隔符:C++14新增,可提高代码可读性。
int moneyInBank=-70'000; // -70000 long populationChange = -85'000; // -85000 long long countryGDPChange = -70'000'000'000; // -70000000000 double pi = 3.141'592'653'59; // 3.14159265359
九,指针声明:通常将指针声明为指向特定的类型,如int,意味着指针包含的地址对饮的内存单元,存储了一个整数。也可以将指针声明为一个内存块,这种指针被称为void指针。
PointedType *PointerVariableName;
与大多数变量一样,除非对指针进行初始化,否则它包含的值将是随机的。如果不希望访问随机的内存地址,可将指针初始化为NULL。NULL是一个可以检查的值,且不会是内存地址:
PointedType *PointerVariableName = NULL; // initializing value
使用引用运算符(&)获取变量的地址,如果varName是一个变量,&varName将是存储该变量的内存地址。可以使用指针来存储地址:
Type VariableName = InitialValue; // 声明一个变量 Type *Pointer = &Variable; // 声明一个该变量类型的指针,指向该变量的内存地址
解除引用运算符(*)用于访问指针指向内存地址的数据。也叫间接运算符。
int age = 10; int *p = &age; // p指向age的内存地址 cout << *p ; // 输出10 *p = 20; cout << age; // 输出20 memset(&age, 5, 1); cout << age; // 输出5 memset(p, 15, 1); cout << age; // 输出15
十,关于sizeof():
1)用于变量时,输出该变量类型的字节数长度;
2)用于指针时,取决于编译器与操作系统。同一种指针的变量类型,不同操作系统下sizeof的长度不同。
3)用于类/类对象时,指出类声明中所有数据属性占用的总内存量,单位为字节。可能会对某些属性进行填充,也可能不会,取决于编译器。但不考虑成员函数及其内部的局部变量。
4)用于结构体时,指出结构体中所有数据属性占用的总内存量,单位为字节。
5)用于共用体时,指出共用体最大成员的长度,即便该成员并不处于活动状态,单位为字节。
十一,动态内存分配和释放
int myNum[100]; // 静态数组,不用手动释放,退出函数块时自动释放
使用new和delete在自由存储区动态分配和释放内存:
Type *Pointer = new Type; // 分配内存 delete Pointer; // 释放内存 Type *Pointer = new Type[numElements]; // 分配内存块给数组 delete[] Pointer; // 释放内存
不能将运算符delete用于任何包含地址的指针,而只能用于new返回的且未使用delete释放的指针。
十二、关键字const用于指针有三种方式:
int* const p;不能修改指针地址,可以修改*p;
const int* p; 不能修改*p,但可以修改指针地址;这种用得最多,避免指针传递时数据被意外修改。
const int* const p;两则都不能修改。
//指针包含的地址是常量,不能修改,但可修改指针指向的数据: int daysInMonth = 30; int* const pDaysInMonth = &daysInMonth; *pDaysInMonth = 31; // OK!Data pointed to can be changed int daysInLunarMonth = 28; pDaysInMonth = &daysInLunarMonth; // Not OK!Cannot change address! //指针指向的数据为常量,不能修改,但可以修改指针包含的地址,即指针可以指向其他地方: int hoursInDay = 24; const int* pointsToInt = &hoursInDay; int monthsInYear = 12; pointsToInt = &monthsInYear; // OK! *pointsToInt = 13; // Not OK!Cannot change data being pointed to int* newPointer = pointsToInt; // Not OK!Cannot assign const to non-const //指针包含的地址以及它指向的值都是常量,不能修改(这种组合最严格): int hoursInDay = 24; const int* const pHoursInDay = &hoursInDay; *pHoursInDay = 25; // Not OK!Cannot change data being pointed to int daysInMonth = 30; pHoursInDay = &daysInMonth; // Not OK!Cannot change address
const char* str; // 与 const int i,没有区别 ,修饰 其所指向的 内存区域 是 只读的,不允许 修改 char * const str; // const 直接修饰 str,即指针变量本身,说明 该指针变量 本身是只读的,但是,其所指向的内存区域还是可以改变的。 char const* str; // 与声明1 本质一致 const char* const str; // 声明1 和声明 2 的 合并,其意义也是 2者的 合并 const char const* str; // 错误的声明。
十三、动态内存分配失败:
1、使用try-catch捕获std::bad_alloc异常:
try{ int* pointsToManyNums = new int[0x1fffffff]; delete[] pointsToManyNums; }catch(std::bad_alloc) { cout << "error.."; }
2,使用new的变种new(nothrow),分配失败不会引发异常,而返回NULL
int* pointsToManyNums = new(nothrow) int[0x1fffffff]; if(pointsToManyNums==NULL) { cout<<"error..."; }else { delete[] pointsToManyNums; }
十三,引用运算符(&),声明引用时需要将其初始化为一个变量,因此引用只是一种访问相应变量的存储数据的方式。
VarType original = Value; VarType& ReferenceVariable = original;
引用使得可以访问相应变量所在的内存单元,从而降低函数调用时参数传递引起的值拷贝,降低内存消耗。
#include "stdafx.h" #include "windows.h" int add(int a) { return a + a; } void addPoint(int* a) { *a = *a + *a; } void addRef(int& a) { a = a + a; } void addPoint(int a, int* out) { *out = a + a; } void addRef(int a, int& out) { out = a + a; } int main() { int num = 10; printf("test add:%d\n", add(num)); // print 20 int* point = # addPoint(point); printf("test addPoint:%d\n", *point); // print 20 num = 10; int& ref = num; addRef(ref); printf("test addRef:%d\n", ref); // print 20 int* out1 = new int; addPoint(10, out1); printf("test addRef:%d\n", *out1); // print 20 delete out1; int ii1 = 0; int& out2 = ii1; addRef(10, out2); printf("test addRef:%d\n", out2); // print 20 int* ii2 = new int; int& out3 = *ii2; addRef(10, *ii2); printf("test addRef:%d\n", *ii2); // print 20 printf("test addRef:%d\n", out3); // print 20 delete ii2; system("pause"); return 0; }
使用const关键字,可以禁止通过引用修改它指向变量的值。且const引用,只能赋值给const引用。
int original = 30; const int& constRef = original; constRef = 40; // Not allowed: constRef can't change value in original int & ref2 = constRef; // Not allowed: ref2 is not const const int& constRef2 = constRef; // OK
相关推荐
C++学习笔记C++学习笔记C++学习笔记C++学习笔记C++学习笔记
C++学习笔记
c++学习笔记/知识要点精华版
c++学习笔记.pdf
C++ 学习笔记C++ 学习笔记C++ 学习笔记C++ 学习笔记
effective c++读书笔记和总结 effective c++读书笔记和总结
千锋C++笔记.zip
c++完美学习笔记c++完美学习笔记c++完美学习笔记c++完美学习笔记
C++相关笔记总结
C++Primer中文第三版(C++从入门到精通)第一章的读书笔记,主要是C++程序、预处理器指示符、iostream库等的基础知识点读书笔记。
【C++学习笔记】一份详细的学习笔记,让你轻松掌握C++编程!
C++ 极好的c++精华笔记,学c++的同学很值得一看
某课网C++远征的课程截图整理的笔记,该课程适合入门,
C++基础的学习笔记(其中的代码源件可私信留言) C++中的空头程序 使用C++书写hello world 学习变量 学习常量 标识符命名规则 数据类型 运算符 程序流程结构 数组 函数 指针 结构体
C++基础笔记
QT 4 c++ 笔记 QT 4 c++ 笔记 QT 4 c++ 笔记 QT 4 c++ 笔记 QT 4 c++ 笔记 QT 4 c++ 笔记
C++学习笔记.docx
C++笔记.rarC++笔记.rarC++笔记.rarC++笔记.rarC++笔记.rarC++笔记.rarC++笔记.rarC++笔记.rar
作者全部手打创作的自考C++笔记,含课本中例子的详细分析,(上)共47200字,就是没有学过C语言的人认真看了以后,也可学会并通过C++自考
《深入理解C++11》读书笔记