从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 ...
随机推荐
- windows 10 + tensorflow-gpu 环境搭建
安装过程可基本按照ubuntu装法,参考https://www.cnblogs.com/xbit/p/9768238.html 其中: conda配置文件:C:\Users\Administrator ...
- VS2017 Debug时候出现 Script Error An error has occurred in the script on this page. 解决办法
解决办法: Menu -> Debug -> Options -> Debugging/General -> 取消最后面的Enable Diagnostic Tools whi ...
- 16 bit 的灰度图如何显示
16 bit 的灰度图如何在QT中显示 用Mat构造的 16 bit 灰度图 无法直接显示,需要转换成 8 bit 的灰度图在QT中显示, 使用OpenCV自带的最大最小值归一法, cv::norma ...
- Linux网络编程:原始套接字简介
Linux网络编程:原始套接字编程 一.原始套接字用途 通常情况下程序员接所接触到的套接字(Socket)为两类: 流式套接字(SOCK_STREAM):一种面向连接的Socket,针对于面向连接的T ...
- 斐波那契数(Java)
斐波那契数,通常用 F(n) 表示,形成的序列称为 斐波那契数列 .该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和.也就是: F(0) = 0,F(1) = 1 F(n) = F(n ...
- Android使用百度语音识别api代码实现。
第一步 ① 创建平台应用 点击百度智能云进入,没有账号的可以先注册账号,这里默认都有账号了,然后登录. 然后左侧导航栏点击找到语音技术 然后会进入一个应用总览页面, 然后点击创建应用 立即创建 点击查 ...
- Java学习笔记--常用容器
容器 1. 出现原因 解决程序运行时需要创建新对象,在程序运行前不知道运行的所需的对象数量甚至是类型的问题. Java中提供了一套集合类来解决这些问题包括:List.Set.Queue.Map 2. ...
- 5-21python数据类型
一.字符串,是不可变数据类型,所有字符串的方法都不会修改字符串的值,使用字符串的方法后都是生成了一个新的字符串.就因为字符串是不可变变量! 字符串的方法 1. strip(),默认去空格,但是当()中 ...
- R和Rstudio的安装
首先是安装R再安装Rstudio 链接放在这里: R语言软件以及Rstudio软件下载:链接:https://pan.baidu.com/s/11TH4mJjoi3QXGfamB697rw 密码:o1 ...
- linux帐户安全管理与技巧
实验环境 CentosOS5.6试验台. 任务一:建立与删除普通用户账户,管理组 1)创建一个新用户user1 useradd user1 查看用户是否创建成功 2)创建一个新组group1 grou ...