`

C++11笔记一

 
阅读更多

一,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 = &num;
	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

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics