c++杂谈-4


一、一些c++11新特性

1. 统一初始化列表
#include <iostream>
#include <initializer_list>

using namespace std;

double sum(initializer_list<double> il) {
	double total = 0;
	for (auto it = il.begin(); it != il.end(); it++)
		total += *it;
	return total;
}

int main(int argc, const char **argv) {

	// 实参要以大括号的方式给出
	double total = sum( { 3.24, 2.56, 7.84 });
	cout << "total = " << total << endl;

	return 0;
}
2. 模版别名

传统的typedef定义别名

typedef std::vector<std::string>::iterator itType;

c++11提供了另一种别名定义

using itType = std::vector<std::string>::iterator;

差别在于,新语法也可用于模板部分特化,但typedef不能:

template <typename T>
	//template for multiple aliases
	using arr12 = std::array<T, 12>;

std::array<double,12> a1;
std::array<std::string,12> a2;
//可将它们替换为如下声明:
arr12<double> a1;
arr12(std::string> a2;
3. 空指针nullptr

空指针是不会指向有效数据的指针。以前,C/C++用0表示空指针,这带来了一些问题,这样的话0既可以表示指针常量,又可以表示整型常量。C++11新增了关键字nullptr,用于表示空指针;它是指针类型,不是整型类型。为了向后兼容,C++11仍允许用0来表示空指针,因此表达式nullptr==0为true。

使用nullptr提供了更高的类型安全。例如,可以将0传递给形参为int的函数,但是,如果将nullptr传递给这样的函数,编译器将视为错误。因此,出于清晰和安全考虑,请使用nullptr。

4. 作用域内枚举

传统的C++枚举提供了一种创建名称常量的方式,但其类型检查相当低级。另外,枚举名的作用域为枚举定义所属的作用域,这意味着如果在同一个作用域内定义两个枚举,它们的枚举成员不能同名。最后,枚举可能不是可完全移植的,因为不同的实现可能选择不同的底层类型。为解决这些问题,C++11新增了一种枚举。这种枚举使用class或struct定义:

enum Old1 {yes, no, maybe}; //traditional form
enum class New1 {never, sometimes, often, always}; //new form
enum struct New2 {never, lever, sever}; // new form

新枚举要求进行显式限定,以免发生名称冲突。因此,引用特定枚举时,需要使用 New1::never 和 New2::never等。

5. 类内成员初始化
clas sGirk {
private:
	int m_bh=20; // 年龄。
	string m_name="美女"; // 姓名。
	char m_xb ='X'; // 性别。
public:
	Girl(int bh,string name): m_bh(bh), m_name(name){};
};
6. final关键字

final关键学用于限制某个类不能被继承,或者某个虚函数不能被重写,final关键字放在类名或虚函数名的后面。

7. 数值类型和字符串的转换

传统方法用sprintf()和snprintf()函数把数值转换为char字符串。用atoi()、atol()、atof()把 char 字符串转换为数值。
C++11提供了新的方法,在数值类型和string字符串之间转换。

  1. 数值转换为字符串
    使用to_string()函数可以将各种数值类型转换为string字符串类型,这是一个重载函数,在头文件<string>中声明,函数原型如下:
string to_string(int val);
string to_string(long val);
string to_string(long long val);
string to_string(unsigned val);
string to_string(unsigned long val);
string to_string(unsigned longl ong val);
string to_string(float val);
string to_string(double val);
string to_string(long double val);
  1. 字符串转换为数值
int stoi(const string& str,size_t* pos=nullptr,int base =10);
long stol(const string& str,size_t* pos=nullptr,int base=10);
long long stoll(const string& str,size_t* pos=nullptr,int base=10);
unsigned long stoul(const string&str,size_t* pos=nullptr,int base=10);
unsigned long long stoull(const string& str,size_t* pos=nullptr,int base=10);
float stof(const string&str,size_t* pos=nullptr);
double stod(const string&str,size_t* pos=nullptr);
long double stold(const string&str,size_t* pos=nullptr);
8. explicit关键字

初始化列表要注意:
1、它按各对象成员在类定义中的顺序(和参数列表的顺序无关)依次调用它们的构造函数。
2、先调用对象成员的构造函数,再调用本身的构造函数。析构函数和构造函数调用顺序相反,先构
造,后析构。

c++提供了关键字explicit,禁止通过构造函数进行的隐式转换。声明为explicit的构造函数不能在隐式转换中使用。
首先了解一下构造函数的几种调用方法。

//调用无参或默认构造(隐式调用)
Data ob1;

//调用无参构造(显示调用)
Data ob2 = Data();

//调用有参构造(隐式调用)
Data ob3(10);

//注意:以下是定义了一个函数,而和定义对象没关系。 
Data ob(); // 定义了一个名为ob且返回值为Data,无参数的函数。

//调用有参构造(显示周用)
Data ob4 = Data(20);

// 使用new就返回指针
Data* p1 = new Data();
Data* p2 = new Data; // 调用默认构造函数时,小括号可以省略。
Data* p3 = new Data(20)

//隐式转换的方式:调用有参构造(针对只有一个数据成员)(尽量别用)
Data ob5 = 30; // 转化成 Data ob5(30);

// 匿名对象(当前语句结束,匿名对象立即释放)
Data(40);

再看explicit关键字的用法

#include <iostream>

using namespace std;

class Data {
private:
	int nu;
public:
	explicit Data(int n) :
			nu(n) {
		cout << "constructor(int n)" << endl;
	}
	~Data() {
		cout << "destructor" << endl;
	}
	void show_nu() {
		cout << "nu = " << nu << endl;
	}
};

int main(int argc, const char **argv) {

	//隐式转换
	// 构造函数加上 explicit 之后阻止隐式转换,编译将报错。
	Data data = 10; // error: conversion from 'int' to non-scalar type 'Data' requested
	data.show_nu();

	return 0;
}

再来一个例子

#include <iostream>

using namespace std;

class MyString {
public:
	explicit MyString(int n) {
		cout << "Mystring(int n)!" << endl;
	}

	MyString(const char *str) {
		cout << "Mystring(const char* str)" << endl;
	}
};

int main(int argc, const char **argv) {

	// 给字符串赋值?还是初始化?
	// 隐式转换为:MyString str1(12);
	MyString str1 = 12; //error: invalid conversion from 'int' to 'const char*' [-fpermissive]
	MyString str2(10);

	//寓意非常明确,给字符串赋值
	MyString str3 = "abcd";
	MyString str4("abcd");

	return 0;
}
9. 默认成员函数控制

在C++中自定义的类,编译器会默认生成一些成员函数:

  • 无参构造函数
  • 拷贝构造函数
  • 拷贝赋值函数
  • 移动构造函数
  • 移动赋值函数
  • 析构函数

=default表示启用默认函数。=delete表示禁用默认函数。

#include <iostream>

using namespace std;

class Girl {
private:
	int m_bh = 20; // 年龄。
	string m_name = "美女"; // 姓名。
	char m_xb = 'X'; // 性别。
public:
	Girl() = default; //启用默认构造函数
	Girl(int bh, string name) :
			m_bh(bh), m_name(name) {
	}
	Girl(const Girl &g) = delete; //删除拷贝构造函数。
	void show() {
		cout << "bh=" << m_bh << ", m_name=" << m_name << endl;
	}
};

int main(int argc, const char **argv) {
	Girl g1;
	g1.show();
	Girl g2 = g1; // 编译出错:拷贝构造函数已删除

	return 0;
}

二、内存分区

在这里插入图片描述

三、产生随机数

#include <cstdlib>
#include <ctime>
#include <iostream>

using namespace std;

int main(int argc, const char **argv) {
	const int max = 10;
	srand(time(nullptr));

	for (int i = 0; i < max; ++i)
		cout << rand() % max << endl;

	return 0;
}

四、register关键字

如果一个变量特别高频繁地使用,编译器会自动将变量存储在奇存器中,其目的是提高访问效率。
如果用户想将变量直接放入寄存器中可以加register关键字修饰。

register int data = 10;	

注意:不能对register修饰的变量进行取地址的操作。register修饰的变量是尽力放到寄存器中——有概率还是放在内存中。寄存器不属于内存,因此不能取地址(会抛异常)。

五、volatile关键字

1, 对于频繁读取的内存变量,编译器会使用寄存器缓存变量的值。
2. 后续CPU是从寄存器中读取值。

由于引入了寄存器缓存,也就引入了延时。为了避免延时,就需要将变量用volatile修饰,达到的效果就是强制CPU去直接读内存中的变量值。

volatile int data=1;

所以 volatile 关键字是在防止编译器优化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值