从c++历程看编程语言的发展
1 简介
C++自1979年首次实现带有类的C以来,已经存在了40年。从了解它的发展历程中去了解语言的发展。
现在其C++ 的语法已经非常复杂。
其发展路径具有重要的参考意义,特别是像go,rust这样的类似的语言。
Rust从C++中学到了内存安全的重要性,Zig从C中选择了更细分的内存分配,Go则带上“指针”和“垃圾收集”两件法宝自成一派。
而其C++26已经在规划之中…
也就是 2026年将要发布的版本。
对于C++26,将努力在该标准中具有以下内容:
* 执行
* 代码范围
* 反射
在没有特定场景的情况下,将优化
- 合约
Contracts 类似 assert 和 static_assert 的语法,可以在函数声明时使用。
- 模式匹配
经常说C++需要像它这样的新控制语句头上需要一孔,图案匹配显示很多希望 提供比我们所能提供的更好的 过滤器/链/选择 近似于纯图书馆设施。
模式匹配 通过以下方式提高类型安全性 使编写类型安全代码比编写 传统的 C 型或传统的C++式替代品更好。
此外,它还将访问者置于 在许多情况下,模式。
这不仅仅是另一个控件结构;它与类型系统交互。
在这个领域有相当多的工作正在进行中,在 C++26 时间范围内给它播出时间。
如何运送,以及 以什么形式,目前还不完全清楚。
做为一个经典版本,C++ 11 出现了大量新特征,几乎成为一个新语言。
这里主要回顾C++11的新的特征。包括新的算法,新的容器类原子操作,类型特征,正则表达式,新的智能指针,async()功能多线程库。
C++11 延迟,
C++14 准时,
C++17 准时 ,
C++20 准时,
C++23 将准时,
C++26 将按时
而C++曾经也是一位昂扬向上的少年,现在只不过是经历了40年之久青年。
1 c++11更新
1.0,基于范围的for循环
int nArr[5] = {1,2,3,4,5};
for(int &x:nArr){
x *=2; //数组每个元素倍乘
}
1.1,Lambda表达式
允许定义本地功能, 消除大部分乏味而且有安全风险的函数对象。
[capture](parameters)->return-type{body}
[]指示lambda表达式的开始
int main()
{
char s [] = "hi";
int Uppercase = 0;
for_each(s, s+sizeof(s), [&Uppercase] (char c)){
if (isupper(c))
Uppercase++;
});
cout<< Uppercase<<"uppercase letters in:" << s<<end1;
}
类似与定义一个函数,将其主主体放置在另一个函数调用。[&Uppercase] &表示lambda主体获得对Uppercase的引用,如果没有&,
大写字母将按值传递,C++11 lambda也包括用于成员函数的构造。
1.2,自动类型推导 decltype, Automatic Type Deduction
编译器自动推断i为int类型
auto i = 1;
在v11 之前C++ 在声音对象时 必须指定对象类型,很多情况下,对象的声明包括初始化程序, C++11充分利用了这一点,可用在不指定对象类型的情况下声明对象。
auto x=0; //x has type int because 0 is int
auto c='a'; //char
auto d=0.5; //double
auto national_debt=14400000000000LL;//long long
auto不再指定具有自动存储类型的对象。相反,它声明一个对象,该对象的类型可从其初始值设定项推导, 声明一个迭代器
auto ci=vi.begin();
标准模板库容器的判断
vector<int> vec(6,10);
vector<int>::iterator iter=vec.iterator();
auto iterAuto = vec.iterator(); //相比较更方便
1.3,统一初始化语法
C++ 11 使用统一的大括号表示初始化
std::string s("hello");
int m=int(); //默认初始化 default initialization
在某些情况下,可用将 = 用于相同目的
std::string s="hello";
int x=5;
对于POD聚合,使用花括号
int arr [4] = {0,1,2,3};
today struct tm = {0};
最后构造函数使用成员初始化程序
structs {
int x;
s():x(0) {}};
}
1.4,删除函数 和 默认函数
默认函数
struct A {
A()=default; // C++11 only 指示编译器生成该函数的默认实现
public: C();
};
默认函数有两个优点:它们比手动实现更有效,并且使程序员摆脱了手动定义这些函数的麻烦。
删除函数
int func()=delete;
已删除的函数对于防止对象复制很有用
=delete:
struct NoCopy
{
NoCopy & operator =( const NoCopy & ) = delete;
NoCopy ( const NoCopy & ) = delete;
};
NoCopy a;
复制出错
NoCopy b(a); //compilation error, copy ctor is deleted
1.5,nullptr
一个关键字,它指定空指针常量。
nullptr替换了NULL多年来容易被用作空指针替代的易于出错的宏和立即数0(macro and the literal)。nullptr是强类型的
void f(int); //#1
void f(char *);//#2
//C++03
f(0); //which f is called?
//C++11
f(nullptr) //unambiguous, calls #2
nullptr 适用于所有指针类别,包括函数指针和成员指针
const char *pc=str.c_str(); //data pointers
if (pc!=nullptr)
cout<<pc<<endl;
int (A::*pmf)()=nullptr; //pointer to member function
void (*pmf)()=nullptr; //pointer to function
1.6,委托建造者 Delegating Constructors
C++11 中构造函数可调用同一类的另一个构造函数
class M //C++11 delegating constructors
{int x, y;
char *p;
public:
M(int v) : x(v), y(0), p(new char [MAX]) {} //#1 target
M(): M(0) {cout<<"delegating ctor"<<endl;} //#2 delegating };
委托构造器#2调用目标构造器#1
1.7, 右值参考
C ++ 03中的引用类型只能绑定到左值。C ++ 11引入了新的引用类型类别,称为rvalue引用。
右值引用可以绑定到右值,例如临时对象和文字
void naiveswap(string &a, string & b)
{string temp = a;
a=b;
b=temp; }
以上例子需要分配内存,使用右值引用可不分配内存,效率更高
void naiveswap(string &a, string & b)
{
伪代码
size_t sz = empty.size();
const char * p = empty.data();
将填充的资源移为空
empty.setsize(filled.size());
empty.setdata(filled.data());
填充为空
fill.setsize(sz);
fill.setdata(p);
}
1.8,新增标准库
TR1标准包括新的容器类
unordered_set, onordered_map,onordered_multiset, nordered_multimap
正则表达式,元组,功能对象包装,文档库
1.8.1,线程库 并发
promise 和 futures 线程类,在并发环境中用于同步的对象
用于启动并发任务的async()
声明线程tread_local存储类型
1.8.2 新的算法
可模仿集合理论运算的新算法 all_of()
none_of()
ispositive() 范围[first,first+n]
并使用all_of(), any_of()
#include <algorithm>
C ++ 11代码,所有元素都是积极的吗?
all_of(first,first + n,ispositive()); //假
至少有一个积极因素?
any_of(first,first + n,ispositive()); //真
这些元素都不是积极的吗?
none_of(first,first + n,ispositive()); //假
复制数组copy_n()
#include
int source [5] = {0,12,34,50,80};
int target[5];
// copy 5 elements from source to target
copy_n(source, 5, target);
iota()创建一系列按顺序递增的值,就像通过将初始值分配给*first
然后使用 ++ 前缀递增该值。 下例中 iota()将连续值{10,11,12,13,14}分配给数组arr,并将{‘a’, ‘b’ ‘c’} 分配给 char数组
include <numeric>
int a[5]={0};
char c[3]={0};
iota(a, a+5, 10); //changes a to {10,11,12,13,14}
iota(c, c+3, 'a'); //{'a','b','c'}
1.9, 后置返回类型 tailng-return-type
template Ret adding_func(const Lhs &lhs, Const Rhs &rhs){return lhs + rhs;}
C++11的合法语法,添加后置返回类型
template auto adding_func(const Lhs &lhs, const Rhs &rhs) -> decltype(lhs+rhs) {return lhs + rhs;}
更普遍的函数声明和定义
struct SomeStruct {
auto func_name(int x, int y) -> int;
};
auto SomeStruct::func_name(int x, int y) -> {
return x + y;
}
1.10, 显示重写 override final
struct Base{
virtual void some_func(float);
};
struct Derived:Base{
virtual void somt_func(int) override; // 错误,不会重写基类方法
};
override 编译器将检查基类中有没有一个具有相同签名的虚函数
final用于防止基类被继承和防止子类重写
struct Base1 final { };
struct Derived1 : Base1 { }; // 病态的, 因为类Base1被标记为final了
struct Base2 {
virtual void f() final;
};
struct Derived2 : Base2 {
void f(); // 病态的, 因为虚函数Base2::f 被标记为final了.
};
-
最大整型
long long int
1.11, 允许sizeof运算符可用在类型数据成员使用,无需明确对象
struct p {otherClass member;};
sizeof(p:member);
1.12, 元组 tuple
由预先确定数量的多种对象组成,元组可用看作时struct数据成员泛化。
使用可变参数模板,元组的定义时这样的
template <class ...Types> class tuple;
下面是定义和使用元组的一个例子:
typedef std::tuple <int, double, long &, const char *> test_tuple;
long lengthy = 12;
test_tuple proof (18, 6.5, lengthy, "Ciao!");
lengthy = std::get<0>(proof); // 把'lengthy' 赋值为18.
std::get<3>(proof) = " Beautiful!"; // 修改元组的第四个元素
1.13,常量constexpr
保证函数或者对象构造函数在编译器常量
constexpr int GetFive() {return 5;}
int some_value[GetFive() + 7];
產生12個整數的陣列。合法的C++11寫法
2 小结
自2011年C++标准的重大修正以来,2020年的C++20被称为又一个里程碑。 而C++26也在计划之中。
C++一般被人认为是C的超集,但是这并不完全准确。
相比C语言,C++早期的新功能就包括
类,成员函数,派生类,单独的编译,
公共和私有访问控制,友元,函数参数的类型检查,默认参数,内联函数,重载运算符,
构造函数,析构函数,调用和返回函数,并发库,虚函数,复数,
引用,命名空间,异常处理,模板,容器,IO流等。
C++20更引入了检查程序实体,例如检查变量,枚举,类及其成员,lambda及其捕获功能等。
大部分C语言代码可以很轻易地在C++正确编译,但是仍有少数差异,导致某些有效C代码在C++失效。
如果需要混用它们,则需要在C++中调用,必须放在 extern “C”{/C代码/}内。
参考
https://en.wikipedia.org/wiki/C++11
https://www.open-std.org/
- 点赞
- 收藏
- 关注作者
评论(0)