从C过渡到C++需要了解的“新特性”
第一个C++程序
#include <iostream>
using namespace std; //编译指令
int main() {
cout << "Hello World!" << endl;
return 0;
}
头文件
在C语言的传统中,头文件使用扩展名.h
//C风格
#include <stdio.h>
老式的C++延续了这个传统
//老式C++
#include <iostream.h>
而新式的C++抛弃了这个传统,转而去掉了.h
//新式C++
#include <iostream>
C++将部分C头文件转换为C++头文件并重新命名:去掉.h
并加上前缀c
#include <cstdio>
在代码中包含cstdio
,C++程序中也可以出现scanf()
和printf()
对于纯粹的C++头文件(比如iostream
)来说,去掉.h
不只是形式上的变化,还可能包含了命名空间
风格 | 约定 | 示例 | C | C++ | 命名空间 |
---|---|---|---|---|---|
C | 以.h 结尾 |
stdio.h |
√ | √ | × |
老式C++ | 以.h 结尾 |
iostream.h |
× | √ | × |
新式C++ | 没有扩展名 | iostream |
× | √ | √ |
转换后的C | 加上前缀c ,没有扩展名 |
cmath |
× | √ | √ |
由于C使用扩展名
.h
来表示这是一个C头文件如果C++头文件继续沿用这个拓展名,容易造成混淆
其中一种解决方案是另起一个扩展名,比如
.hpp
等。但最后大家却一致同意不使用任何扩展名 可能是懒的选了
编译指令
如果你使用的头文件是iostream
,而不是iostream.h
那么你应该使用下面的语句来使iostream
中的定义可用
using namespace std;
这句话告诉编译器,使用std
这个命名空间,这样可以更方便的使用cout
关于命名空间的详细内容,留在了文章末尾的拓展部分。对于刚刚开始转向C++的萌新来说,最简单的方法就是先记住要写这句话就完了。
cout
cout << "Hello World!" << endl;
什么是cout
?
cout
是一个预定义的对象(还没有提对象的概念,暂且把它当成一个东西、一个物件)。cout
知道如何在屏幕上显示字符串、数字、单个字符等。
cout << 56;
cout << ' ';
cout << 9.10;
这个<<
又是什么?
<<
是C位运算的左移操作符,用来操作位。
在C++有了新的用途——输出。箭头方向指明了信息流动的方向。
信息可以流向cout
从屏幕上输出,也可以通过键盘输入流向某个变量
cin >> a;
箭头方向指向a
,说明这个信息是流向了a
。
endl
是一个控制符,相当于C里面的换行符\n
面向对象
C++融合了3种不同的编程方式
- 面向过程
(Procedure Oriented Programming)
- 面向对象
(Object Oriented Programming)
- 泛型编程
(Generic Programming)
通过C语言的学习,相信你已经对面向过程有所了解。假如我们现在要用程序来模拟一次上课的场景,用面向过程的思维来设计的话就是:
- if (时间 == 8:00) 上课
- 老师讲课,学生听课
- 进入循环
- ……
- if (时间 == 9:40) 下课
那如果我们要用面向对象的思维来模拟上课场景,该怎么做?
什么是对象
面向对象在台湾有另一种叫法——物件导向。所谓的对象其实就是我们常说的东西,对象可以是
- 一个按钮
- 一盏灯
一个对象由下面两样东西组成
- 数据(
Data
) - 操作(
Operations
)
以一盏灯为例
Properties
可以是灯丝,Status
是灯是否开启、灯的颜色。Operations
可以是开关,这个操作会改变内部的Data
上面这副蛋图,蛋黄位置的Data
被Operations
所包裹。想要碰到Data
只能穿过Operaions
。
为什么要把Data
包裹起来?举个栗子,电视机的外壳。这个外壳除了让产品更好看之外,更重要的是保护着电视内部的电气元件,通常外壳上都会贴一个警告标签
非专业人士请勿拆卸
显然厂家不希望用户拆开这个外壳,如果用户拆开了外壳,自己去拔插电视内部的线路,很有可能会损坏电视。
但这个外壳会影响我们的使用吗?当然不会,设计者留出了很多按钮开关来操作电视机,这些按钮开关就是我们说的Operations
。
在OOP的世界里,大致有两类程序员
- 设计类的程序员(设计者)
- 使用类的程序员(用户)
对于设计者来说,用户不能直接碰到内部数据。用户只能通过设计者给出的接口来访问内部数据。
对于用户来说,用户关心的是功能而不是原理。正如电视机的按钮,按下就可以启动,用户不需要了解其中的电子电路知识。设计者对用户屏蔽了具体实现细节。
把数据和数据的操作放在一起,形成有机联系,叫做封装。
所以,如果我们要用面向对象思维来模拟一个上课过程,我们要做的就是写对象
- 一个老师对象
- 若干个学生对象
这些对象的互动就是上课的过程。
从对象到类
- class
a collection of things sharing a common attribute.
- object
entity
object
是实体,class
是概念
小明是一个学生对象,他的名字是 小明
,期末考成绩90
从小明身上抽象出学生的一些共有属性,可以得到下面的类
class Student{
int sid;
int score;
};
另外有一个学生对象小红
,她也会有学生类里面的属性
所以,class
定义了object
该长什么样,而object
里面有具体的属性。
OOP的五项原则
- Everything is an object.
- A program is a bunch of objects telling each other what to do by sending messages.
程序是由一堆互相之间传递消息的对象组成的。与之对应的是,面向过程语言(例如C)设计出来的程序是由一堆函数组成的。 - Each object has its own memory made up of other objects.
每一个对象有他自己的内存,这些对象里面还可以有对象。 - Every object has a type.
每个对象有一个自己的类型。 - All objects of a particular type can receive the same messages.
一个特定类型的对象可以接受相同的信息。反过来也可以说,能接受相同消息的对象可以认为是一样的对象。
结构体和类
//这是一个C结构体的标签
struct Student{
int sid;
int score;
};
用C语言定义这样一个结构体要在前面加struct
//C风格的定义
struct Student zs;
用C++定义时可以省略struct
关键字
//C++风格的定义
Student zs;
同样的事情在C语言中实现要用到typedef
typedef struct _Student{
int sid;
int score;
}Student;
C++的结构体标签中还可以定义函数,这在C语言是不被允许的行为
//C++ 包含了操作方法的结构体
struct Student{
int sid;
int score;
void haha( void ){
cout << "haha..." << endl;
}
};
结合前面提到的OOP原理
C++的结构体可以放操作方法,有数据又有操作方法,所以C++中的结构体也是一个类class
struct Student{
int sid;
int score;
// 操作方法
void haha( void ){
cout << "haha..." << endl;
}
};
上面的struct
可以改写成下面的class
class Student{
public:
int sid;
int score;
void haha( void ){
cout << "haha..." << endl;
}
};
访问控制
public
如果声明类的某个成员为public
,那么这个成员可以被类外面的语句、函数随便使用。以Student
类为例,其全部成员声明为public
,可以直接修改zs
的数据
Student zs;
zs.sid = 007;
private
改写Student
class Student{
private:
int sid;
int score;
public:
void haha( void ){
cout << "haha..." << endl;
}
};
现在sid
和score
是这个类所私有的成员,此时不允许在外部用zs.sid = 007
来修改内部数据,必须是类里面的成员才能修改它们。
class Student{
private:
int sid;
int score;
public:
void haha( void ){
cout << "haha..." << endl;
}
void get_sid( void ){ //修改sid的接口
cin >> sid;
}
};
如果省略了public
和private
关键字
struct
默认全部成员公开class
默认全部成员私有
布尔值
在C99之前,C语言是没有表示真假的布尔值,统统用int
#define TRUE 1
#define FALSE 0
int flag = 5 > 3;
flag
的值只有0
和1
两种情况
C语言把非0
当作真,0
当作假,在C++也是一样的
//测试用例
if (3) {
cout << 1 << endl;
}
if (-2) {
cout << 2 << endl;
}
//屏幕输出
1
2
C++提供了bool
型变量,bool
只占用一个字节的空间,用true
和false
这两个字面值常量来表示真假。
bool flag = true;
flag = false;
用int
类型的数据来给bool
类型赋值时
- 非0的认为是
true
- 0认为是
false
bool flag = 123;
cout << flag << endl;
//屏幕输出
1
auto
C++是一门强类型的语言,声明变量的时候必须清楚地知道表达式的类型,然而要做到这一点并非容易。
auto
关键字让编译器自己去推断类型。和int
这些特定类型不同,auto
定义的变量必须有初始化。
auto a = 3;
a /= 2;
cout << a << endl;
//屏幕输出
1
用3
来给a
初始化,编译器推断a
为int
auto a = 3.0;
a /= 2;
cout << a << endl;
cout << sizeof(a) << endl;
//屏幕输出
1.5
8
用3.0
给a
初始化,编译器推断出a
为double
类型。
引用 Reference
引用是已经定义的变量的别名
int b = 1;
int &r = b;
声明引用变量时必须进行初始化定义引用时,引用一旦创建好之后引用来源不能改变。
引用示例
C函数的参数传递,要么是值传递,要么是指针传递。
//C版本的交换函数
void swaq( int *a, int *b ) {
int temp;
temp = *a;
*a = *b;
*b = temp;
}
C++的参数传递不仅仅是值,还可以是引用。所以可以把交换函数的参数改为引用。
void swaq(int& a, int& b) {
int temp;
temp = a;
a = b;
b = temp;
}
在C代码中。看到func(a)
可以放心的肯定func()
得到的是一个值的拷贝,a
原本的值不会被修改。
而到了C++,参数传递还可以传引用,看到func(a)
不能简单地下结论。
如果你不希望参数引用的变量被修改,应该使用const
void func(const int& a);
右值引用
使用下面的语句给一个右值创建引用
const int& r = 123;
int&& r = 123; //右值引用
简要了解即可
赋值
C++可以编译下面的代码
(a = 3) = 666;
//屏幕输出
666
- 在C语言中,
(a = 3)
的值为3
,3
是一个常量,常量不能做左值 - 在C++,
(a = 3)
是a
的引用,可以继续赋值。
基于范围的for循环
int a[5] = { 1,2,5,3,4 };
for (int i : a) {
cout << i << ' ';
}
//屏幕输出
1 2 5 3 4
依次打印出数组a的5个元素
加上&
表示引用后,可以修改元素的值
for (int& i : a) {
i++;
}
for (int i : a) {
cout << i;
}
//屏幕输出
2 3 6 4 5
动态内存分配
申请内存
malloc()
接受一个数值,指明要申请几个字节
//C版本
int size = 10;
int* p;
p = (int*)malloc(sizeof(int) * size);
到了C++,程序员不用自己数数了,直接指明我要什么,程序自己计算要多少空间。
//C++版本
p = new int[size];
释放内存
//C版本
free(p);
//C++版本
delete[] p;
new
了之后一定要记得delete
,有借有还再借不难。
加上 [] 释放整个数组,而不是只释放p[0]
重载 Overload
函数重载
C++可以编译下面的代码
int add( int x, int y );
int add( int x, int y, int z );
有两个函数名相同但参数列表不同的函数,在编译时编译器就会选取符合参数列表的函数。
错误重载1:相同的原型
但下面的代码不能通过编译
int add( int x, int y );
int add( int y, int z );
虽然参数的名字不相同,但两者的原型是一样的,都是
int add( int, int );
无法重载。
错误重载2:重载返回类型
下面的代码也不能通过编译
int add( int x, int y );
void add( int x, int y );
仅仅是返回类型不一样,编译器无法判断究竟重载哪个函数。
无法重载。
错误重载3:有歧义的重载
int add(int x, int y) {
return x + y;
}
double add(double x, double y) {
return x + y;
}
int main(void) {
cout << add(1, 2.5) << endl;
return 0;
}
main函数里调用add
的语句有歧义。1可以转换为double
,2.5也可以转换为int
,编译器不知道该重载哪个。
操作符重载
现在有一个向量Vector
struct Vector {
int x;
int y;
};
向量的加法
(
x
1
,
y
1
)
+
(
x
2
,
y
2
)
=
(
x
1
+
x
2
,
y
1
+
y
2
)
(x_1,y_1)+(x_2,y_2) = (x_1+x_2,y_1+y_2)
(x1,y1)+(x2,y2)=(x1+x2,y1+y2)
+
号只能对整数浮点数求值,通过重载运算符+
,用+
就可以实现向量加法
Vector operator +(const Vector& a, const Vector& b) {
Vector c;
c.x = a.x + b.x;
c.y = a.y + b.y;
return c;
}
int main() {
Vector a, b;
a.x = 3;
a.y = 4;
b.x = 1;
b.y = 5;
Vector sum;
sum = a + b;
cout << sum.x << " " << sum.y << endl;
return 0;
}
lambda表达式
auto f = []( int a, int b )-> int{ return a + b; };
这是函数的一种写法
- f是一个函数指针
( int a, int b )
是参数列表{}
前面写函数的返回类型{}
里面写函数的内容。
扩展
命名空间可以帮助我们避免不经意的名字定义冲突,以及使用库中相同名字导致的冲突。标准库定义的所有名字都在命名空间std
中。
通过命名空间使用标准库,当使用标准库中的一个名字时,必须使用::
显式说明。
std::cout << "Hello World!" << std::endl;
要使用命名空间,头文件iostream
不能有.h
using
编译指令使得std
命名空间里的所有名称都可用
之后都不需要使用std::
从C过渡到C++需要了解的“新特性”的更多相关文章
- Css3新特性应用之过渡与动画
目录 背景与边框第一部分 背景与边框第二部分 形状 视觉效果 字体排印 用户体验 结构与布局 过渡与动画 源码下载 一.缓动效果 学习和利用贝塞尔曲线,默认支持ease,ease-in,ease-ou ...
- CSS3 新特性(box-sizing盒模型,背景线性渐变,filter滤镜,calc函数,transition过渡)
1.盒子模型(box-sizing) CSS3 中可以通过 box-sizing 来指定盒模型,有两个值:即可指定为 content-box.border-box,这样我们计算盒子大小的方式就发生了改 ...
- CSS3新特性—过渡、转换
过渡 转换 2D转换 2D转换包括四个方面:位移,缩放,旋转,倾斜 位移[让元素移动位置] transform: translate(100px,100px); 备注: 1. 如果只设置一个值,那么代 ...
- ios项目里扒出来的json文件
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 13.0px Menlo; color: #000000 } p.p2 { margin: 0.0px 0. ...
- Github上关于iOS的各种开源项目集合(强烈建议大家收藏,查看,总有一款你需要)
下拉刷新 EGOTableViewPullRefresh - 最早的下拉刷新控件. SVPullToRefresh - 下拉刷新控件. MJRefresh - 仅需一行代码就可以为UITableVie ...
- iOS及Mac开源项目和学习资料【超级全面】
UI 下拉刷新 EGOTableViewPullRefresh – 最早的下拉刷新控件. SVPullToRefresh – 下拉刷新控件. MJRefresh – 仅需一行代码就可以为UITable ...
- iOS:iOS开发非常全的三方库、插件等等
iOS开发非常全的三方库.插件等等 github排名:https://github.com/trending, github搜索:https://github.com/search. 此文章转自git ...
- iOS开发--iOS及Mac开源项目和学习资料
文/零距离仰望星空(简书作者)原文链接:http://www.jianshu.com/p/f6cdbc8192ba著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”. 原文出处:codecl ...
- iOS、mac开源项目及库汇总
原文地址:http://blog.csdn.net/qq_26359763/article/details/51076499 iOS每日一记------------之 中级完美大整理 iOS.m ...
随机推荐
- 由Eratosthenes筛法演变出的一种素数新筛法
这两天和walls老师交流讨论了一个中学竞赛题,我把原题稍作增强和变形,得到如下一个题: 从105到204这100个数中至少要选取多少个数才能保证选出的数中必有两个不是互素的? 我们知道最小的几个素数 ...
- linux centos7 获取开机时间
2021-08-03 1. who 命令 who 命令显示关于当前在本地系统上的所有用户信息:登录名,线路,时间,备注 # 列出当前登录本系统的用户 who # 列出本系统的开机/重启时间 who - ...
- mysql绕过root密码登录
绕过密码登录步骤: 一.Mysql8.0之前: 关闭服务 执行参数:mysqld --skip-grant-tables 新开窗口执行mysql,即可进入mysql 二.Mysql8.0之前: 关闭服 ...
- SSH无法正常连接服务器
远程权限没有打开 #允许root登录 PermitRootLogin yes #不允许空密码登录 PermitEmptyPasswords no 远端的ssh信息有变化,本地保存的那个需要删掉 Use ...
- Sentry Web 性能监控 - Web Vitals
系列 1 分钟快速使用 Docker 上手最新版 Sentry-CLI - 创建版本 快速使用 Docker 上手 Sentry-CLI - 30 秒上手 Source Maps Sentry For ...
- Request请求对象
一.Request对象由服务器创建,我们使用 浏览器访问服务器资源原理: 二.Request体系结构 其中,servlet 的service()方法参数列表是 servletRequest对象, Ht ...
- DHCP的原理和配置
前言 在大型企业网络中,会有大量的主机或设备需要获取IP地址等网络参数.如果采用手工配置,工作量大且不好管理,如果有用户擅自修改网络参数,还有可能会造成 IP地址冲突等问题.使用动态主机配置协议DHC ...
- 生产环境部署高可用Rancher
环境准备: IP hostname role 192.168.200.150 nginx LB 192.168.200.151 master01-151 docker-ce/rke/helm/kube ...
- Excel删除重复数据及用公式筛选重复项并标记颜色突出显示
当表格记录比较多时,常常会有重复数据,而重复记录往往只希望保存一条,因此需要把多余的删除:在 Excel 中,删除重复数据有两种方法,一种是用"删除重复数据"删除,另一种是用&qu ...
- js根据日期获取所在周
一.获取时间所在周的周一.周五 function getFirstLastDay (time) { let date = new Date(time) let Time = date.getTime( ...