C++ 语言对比


本章内容概述

C++语言相比于C语言,加入了许多新的特性,两者的编码思想也有许多区别,本文将对C++的一些新特性进行列举和分析。


一、C和C++对比

C语言是典型的面向过程语言,C++则是典型的面向对象语言。面向过程是指,以过程为中心,侧重总解决问题的步骤,使用函数依次实现步骤。面向对象则是侧重于将构成问题的事务分解成为各个对象,描述问题的某些行为,进而解决问题,典型特征是封装、继承、多态。

C++继承了C语言的底层特性,同时加入了面向对象机制、继承、值传递与引用传递的区分,以及const关键字等,目前C++完全兼容C语言语法。

1.相同点

C++完全兼容C语言,关键字和运算符基本相同,二者内存模型与硬件接近,都存在栈、堆、静态变量等概念。

2.不同点

C是完全面向过程的语言,不支持面向对象,不支持封装、继承、多态。

C++类型检查更为严格。在C中,类型转换不被严格检查,但是在C++中,部分强制类型转换甚至不被允许。

C中的结构体不支持成员函数,且不支持权限控制,对所有人可见;C++中的类可以具有自己的成员函数,并对其进行访问权限控制。

C++增加了面向对象机制泛型编程机制异常处理引用运算符重载标准模板库命名空间

二、C++ 11 新特性

C++ 11 相比于C++ 98 引入了很多新特性,本章介绍常见的一些特性。

1.auto类型推导

auto关键字,可以进行自动类型推导,即在编译期间,通过初始化语句或函数返回值,推导出变量应为何种类型,基本语法如下:

auto v = v1 + v2;  //根据v1 v2推断 v 的类型
auto f = func();  //根据func函数返回值类型推断 f 的类型

需要注意的是,auto类型推导变量声明时必须完成初始化赋值,否则无法进行类型推导

在类型推断过程中,会遵照具体规则进行推断:去除引用,去除顶层修饰,如const、volatile,即,无法自动推导出const或volatile类型,需要显示声明。且auto对地址推导的类型为指针,数组名在初始化表达式中会自动隐式转换为首元素地址的右值。综上,编译器会适当改变推导类型,以符合初始化规则。

2.decltype类型推导

decltype,即"declare type",声明类型,与auto功能类似,都可以进行自动类型推导,但是decltype表名,只使用表达式的值类型推断出定义的变量类型,但并不使用表达式的值进行初始化,因此decltype可以先定义,再初始化,但auto不可以,代码如下:

decltype(v1 + v2) v;  //利用v1 + v2的类型推导 v 的类型

decltype推导规则如下:

T e;
decltype(e) x;

若e为无括号变量、函数参数、类成员访问,则返回类型为该变量或参数的声明类型;
否则,根据表达式分类:若e为左值,则返回T&;若e为临终值,则返回T&&;若e为右值,则返回T

3.lambda表达式

lambda 表达式定义了一个可以捕获一定范围内的变量的匿名函数,函数语法如下:

[capture list] (parameter list) -> return type
{
	function body;
};

其中,capture list为捕获参数列表,可以按照指定形式捕获外部参数,一般称作“闭包”,常见用法如下:

[]      	// 沒有定义任何变量。使用未定义变量会引发错误。
[x, &y] 	// x以传值方式传入(默认),y以引用方式传入。
[&]     	// 任何被使用到的外部变量都隐式地以引用方式加以引用。
[=]     	// 任何被使用到的外部变量都隐式地以传值方式加以引用。
[&, x]  	// x显式地以传值方式加以引用。其余变量以引用方式加以引用。
[=, &z] 	// z显式地以引用方式加以引用。其余变量以传值方式加以引用。

需要注意的是,按值捕获的变量不允许修改,除非有mutable修饰,但即便修改后,也对外部参数无影响,可以理解为,在被捕获时,就拷贝了一份新的数据,与外界数据无关。

参考用例如下:

int a = 10;
int b = 20;

auto f = [a, &b](int x)-> int
{
	//a++; 此处报错,a为按值捕获,无法修改
	b++;
	return a + b + x;
};

cout << a << endl;		// 10
cout << b << endl;		// 20
cout << f(5) << endl;	// 36
cout << a << endl;		// 10
cout << b << endl;		// 21

对参考用例进行修改,使用mutable修饰a,代码如下:

int a = 100;
int b = 20;

auto f = [a, &b](int x) mutable -> int
{
	a += 100;
	b++;
	return a + b + x;
};

cout << a << endl;
cout << b << endl;
cout << f(5) << endl; 	//226
cout << a << endl; 		//100
cout << b << endl; 		//21
cout << f(5) << endl; 	//327
cout << a << endl; 		//100
cout << b << endl; 		//22

可以观察到,按值捕获的a在函数内部不断增长,但外部的a不变;按引用捕获的b在函数内部的增长也改变了外部的b。

4.范围for语句

笔者此处认为范围for语句是类似于python中for的用法的,使用者甚至无需知道被遍历容器存储的数据类型和长度,即可遍历容器,具体使用方法如下:

for (declaration : expression)
{
    statement
}

expression 为被遍历的容器或序列,declaration 为被遍历容器中单位元素的数据类型定义变量,是使得每一个被遍历到的元素都可转化为该类型变量,常用auto自动推导,使用范例如下:

string s = "Hello World";

for (auto c : s)
{	cout << c;	}
//Hello World

5.右值引用

C++中的值分类属性为“左值”和“右值”,其中左值指内存中有确定地址存储的对象的值,右值,则是所有不属于左值的值。右值,一般可以为临时对象、字面量等。能否被赋值不是区分左值和右值的关键,常量左值不能被赋值,但仍然是左值;临时对象右值可以被赋值,但仍然是右值。两者的根本区别在于,左值可以被取地址获得内存地址。

右值的产生是为了提高C++ 中按值传递效率,无需调用拷贝函数依次拷贝各成员数据,而是直接选择使用移动构造函数进行临时对象的复制,通过改变地址绑定,提高复制效率,代码如下:

int var = 42;
int &l_var = var;
int &&r_var = var; // error: cannot bind rvalue reference of type 'int&&' to lvalue of type 'int' 错误:不能将右值引用绑定到左值上
int &&r_var2 = var + 40; // 正确:将 r_var2 绑定到求和结果上
int &&r_var3 = std::move(var) // 正确

6.标准库move函数

move() 函数,通过该函数可获得绑定到左值上的右值引用。通过 move 获取变量的右值引用,从而可以调用对象的移动拷贝构造函数和移动赋值构造函数。

通过右值引用,可以在不消耗额外资源的情况下完成对象的拷贝,提高了效率。

7.使用和禁用对象默认函数

C++ 11允许显式声明采用或拒绝编译器提供的内置函数,代码如下:

class test
{
public:
	test() = default; 					//采用默认构造
	~test() = default; 					//采用默认析构
	test(test& x) = delete;				//拒绝拷贝构造
	test& operator=(test& x) = delete;	//拒绝重载赋值运算符
};

8.constexpr

一般情况下,常量表示式不能含有函数调用或对象构造,代码如下:

int test(){ return 5; }
int arr[test()+1]; //非法

因为编译器无从得知 test() 的返回值是否为常量,所以拒绝这样定义。在用constexpr 修饰后,编译器会去主动验证 test() 返回值类型,从而接收此类定义。

同时,用 constexpr 修饰函数将限制函数的行为,如:函数的回返值类型不能为void,函数体不能声明变量或定义新的类型,函数体只能包含声明、null语句或者一段return语句,函数的内容必须依照 “return expr” 的形式,在参数替换后,expr 必须是个常量表达式,这些常量表达式只能够调用其他被定义为 constexpr 的函数或其他常量形式的参数。C++ 14以后,规则有所改动。

9.nullptr

C++ 11 引入了新的关键字来代表空指针常量:nullptr。在这之前,NULL可以表示空指针,也可以被定义为0,存在歧义,因此,使用 nullptr 将空指针和整数 0 的概念拆开,nullptr 的类型为 nullptr_t,能隐式转换为任何指针或是成员指针的类型,也能和它们进行相等或不等的比较。

三、C++ 14 特性

1.函数返回值类型推导

函数返回值类型可以使用auto类型,编译时由编译器进行类型推导,代码如下:

auto func()
{	return 1;	}

需要注意的是,如果使用函数返回值类型推导,则对函数返回值有一定要求:若由多个返回语句,则返回类型必须相同;不能返回初始化列表;虚函数不能使用返回值类型推导;可以出现在递归函数中,但必须以至少一个返回语句作为先导。

2.lambda表达式

在C++ 14中,lambda表达式的形参允许使用auto,且允许lambda捕获列表中对变量进行表达式赋值,支持定义新变量和初始化,代码如下:

auto func = [value = 1](auto x, auto y) -> int{	return value + x + y; 	}

3.变量模板

支持对变量进行模板化编辑,使得变量在不同类型下返回不同值,代码如下:

template<class T>
constexpr T pi = T(3.1415926535)

cout << pi<int> << endl;		//3
cout << pi<double> << endl;		//3.14159

4.deprecated属性

deprecated关键字可以标注不推荐使用的实体,使得在调用被标注的实体时由编译器发出警告,代码如下:

[[deprecated]] int f();

本章总结

本章主要讨论了C++ 11的部分新特性以及与C语言的不同之处,是面试中经常出现的问题,需要熟稔于心。

最后,我是Alkaid#3529,一个追求不断进步的学生,期待你的关注!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alkaid3529

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值