如果你也是从C语言一路过来的,那么请一起看下去吧!
文章目录
- 面型对象程序设计
- C++基础
- C和C++一些语法区别
- C++在非对象方面对C语言的扩充
- C++的一些标准(兼容旧标准)
首先,在C++的学习中,我们要时刻清醒一点:虽然C++与C语言有很大区别,但C++是C语言的扩充!
说到C++和C语言的区别,那也是面向对象和面向过程的区别,这时你的老师可能会这么给你讲:
可是学了C语言没学过C++的人发问了:我们在学面向过程设计的时候,说“数据和操作往往都是分离的”,数据是米饭,操作是米饭上盖的菜,既然如此面向过程那不应该是盖饭吗?而面向对象具有封装的特点更是炒饭呀!(老师气的脸都绿了)
只有当你亲口尝了这两种饭菜,你才会知道盖饭中的菜是什么米是什么,炒饭中的菜是什么米是什么,以及他们的口味。所以,当你正确的理解了这个比喻,你便能真正认识C语言与C++,面向过程和面向对象,那么请跟随我一起去寻找我们的答案吧~
面型对象程序设计
面向过程是这样的:
一个程序是一道菜,面向过程这道菜的特点是:先从整体角度全面看待问题,然后列出解决问题需要的步骤,再逐步去完善。
面向对象是这样的:
面向对象这道菜有两个主要的结构特点:①程序一般由类的定义(属性)和类的使用(行为)两部分组成;②程序中的一切都是通过向对象发送消息来实现的,对象接收到消息后,启动有关方法完成相应的操作。
- 对象:描述其属性的数据以及对这些数据施加的一组操作封装在一起构成的统一体。对象可认为是数据+操作。
- 类:类是具有相同的数据和相同的操作的一组对象的集合。
- 消息传递:对象之间的交互。
- **方法:**对象实现的行为称为方法
面向对象程序设计的基本特征:抽象、封装、继承、多态。
理清了面向对象和面向过程之间的区别后,然我们来了解一下——从C语言到C++~
C++基础
C++的产生和特点
C++是美国贝尔实验室的Bjarne Stroustrup博士在C语言的基础上,弥补了C语言存在的一些缺陷,增加了面向对象的特征,于1980年开发出来的一种面向过程性与面向对象性相结合的程序设计语言。最初他把这种新的语言称为“含类的C”,到1983年才取名为C++。
相比C语言,C++的主要特点就是增加了面向对象机制!
一个简单的C++示例程序
#include<iostream> //编译预处理命令
using namespace std; //使用命名空间
void Log(const char* message) { //函数定义
cout << message << endl;
}
int main() {
Log("Hello World!");
cin.get();
}
C和C++一些语法区别
1.三目运算符:在C语言中返回的是一个常量,是不能被赋值的;而C++中返回的是变量,可以被赋值
2.C++中的函数必须要写返回值类型
3.在全局下,C++不允许int a;
和int a=10;
等这种重定义二义性操作
4.在C++中不要返回局部变量或临时变量的地址,虽然C++能够运行变量出作用域之后再使用一次,即C++允许在代码块中的任何地方声明局部变量。
5.C语言中const修饰的变量不能定义数组大小,而C++中可以
const int a=10;
float arr[a];
12
6.C++中定义结构体类型变量可以不用写结构体类型名
7.关于C语言有没有bool类型:C语言的布尔类型(bool)
C++在非对象方面对C语言的扩充
(1)输入和输出
int i;
float f;
cin >> i;
cout << f;
------------
scanf("%d", &i);
printf("%f", f);
----------------
连续读入
cin >> a >> b >> c;
【cin】
- 在默认情况下,运算符“
>>
”将跳过空白符,然后读入后面与变量类型相对应的值。因此,给一组变量输入值时可用空格符、回车符、制表符将输入的数据间隔开。 - 当输入字符串(即类型为string的变量)时,提取运算符“
>>
”的作用是跳过空白字符,读入后面的非空白字符,直到遇到另一个空白字符为止,并在串尾放一个字符串结束标志‘\0
’。
(2)const修饰符
在C语言中,习惯使用#define
来定义常量,例如#define PI 3.14
,C++提供了一种更灵活、更安全的方式来定义常量,即使用const
修饰符来定义常量。例如const float PI = 3.14;
const可以与指针一起使用,它们的组合情况复杂,可归纳为3种:指向常量的指针、常指针和指向常量的常指针。
-
指向常量的指针:一个指向常量的指针变量。
const char* pc = "abcd"; 该方法不允许改变指针所指的变量,即 pc[3] = ‘x'; 是错误的, 但是,由于pc是一个指向常量的普通指针变量,不是常指针,因此可以改变pc所指的地址,例如 pc = "ervfs"; 该语句付给了指针另一个字符串的地址,改变了pc的值。 123456
-
常指针:将指针变量所指的地址声明为常量
char* const pc = "abcd"; 创建一个常指针,一个不能移动的固定指针,可更改内容,如 pc[3] = 'x'; 但不能改变地址,如 pc = 'dsff'; 不合法 12345
-
指向常量的常指针:这个指针所指的地址不能改变,它所指向的地址中的内容也不能改变。
const char* const pc = "abcd"; 内容和地址均不能改变 12
说明:
- 如果用const定义整型常量,关键字可以省略。即
const in bufsize = 100
与const bufsize = 100
等价;- 常量一旦被建立,在程序的任何地方都不能再更改。
- 与#define不同,const定义的常量可以有自己的数据类型。
- 函数参数也可以用const说明,用于保证实参在该函数内不被改动。
(3)void型指针
void通常表示无值,但将void作为指针的类型时,它却表示不确定的类型。这种void型指针是一种通用型指针,也就是说任何类型的指针值都可以赋给void类型的指针变量。
需要指出的是,这里说void型指针是通用指针,是指它可以接受任何类型的指针的赋值,但对已获值的void型指针,对它进行再处理,如输出或者传递指针值时,则必须再进行显式类型转换,否则会出错。
void* pc;
int i = 123;
char c = 'a';
pc = &i;
cout << pc << endl; //输出指针地址006FF730
cout << *(int*)pc << endl; //输出值123
pc = &c;
cout << *(char*)pc << endl; //输出值a
(4)内联函数
在函数名前冠以关键字inline
,该函数就被声明为内联函数。每当程序中出现对该函数的调用时,C++编译器使用函数体中的代码插入到调用该函数的语句之处,同时使用实参代替形参,以便在程序运行时不再进行函数调用。引入内联函数主要是为了消除调用函数时的系统开销,以提高运行速度。
说明:
- 内联函数在第一次被调用之前必须进行完整的定义,否则编译器将无法知道应该插入什么代码
- 在内联函数体内一般不能含有复杂的控制语句,如for语句和switch语句等
- 使用内联函数是一种空间换时间的措施,若内联函数较长,较复杂且调用较为频繁时不建议使用
#include <iostream>
using namespace std;
inline double circle(double r) //内联函数
{
double PI = 3.14;
return PI * r * r;
}
int main()
{
for (int i = 1; i <= 3; i++)
cout << "r = " << i << " area = " << circle(i) << endl;
return 0;
}
使用内联函数替代宏定义,能消除宏定义的不安全性
(5)带有默认参数值的函数
当进行函数调用时,编译器按从左到右的顺序将实参与形参结合,若未指定足够的实参,则编译器按顺序用函数原型中的默认值来补足所缺少的实参。
void init(int x = 5, int y = 10);
init (100, 19); // 100 , 19
init(25); // 25, 10
init(); // 5, 10
1234
-
在函数原型中,所有取默认值的参数都必须出现在不取默认值的参数的右边。
如 int fun(int a, int b, int c = 111); 1
-
在函数调用时,若某个参数省略,则其后的参数皆应省略而采取默认值。不允许某个参数省略后,再给其后的参数指定参数值。
(6)函数重载
在C++中,用户可以重载函数。这意味着,在同一作用域内,只要函数参数的类型不同,或者参数的个数不同,或者二者兼而有之,两个或者两个以上的函数可以使用相同的函数名。
#include<iostream>
//以下函数均构成重载
int plus(int a, int b) {
return a + b;
}
int plus(int a, int b, int c) {
return a + b + c;
}
double plus(double a,double b) {
return a + b;
}
double plus(int a, double b) {
return a + b;
}
double plus(double a, int b) {
return a + b;
}
int main() {
std::cout << plus(2, 3) << std::endl;
std::cout << plus(1,2,3) << std::endl;
std::cout << plus(2.5, 5.3) << std::endl;
std::cout << plus(3,4.5) << std::endl;
std::cout << plus(3.3,6) << std::endl;
}
说明:
-
调用重载函数时,函数返回值类型不在参数匹配检查之列。因此,若两个函数的参数个数和类型都相同,而只有返回值类型不同,则不允许重载。
int mul(int x, int y); double mul(int x, int y); 12
-
函数的重载与带默认值的函数一起使用时,有可能引起二义性。
int fun(int a){}; int fun(float a){};
从函数重载的重载条件来看,构成重载,编译不会有问题,但在调用时,如果调用方式如:fun(2.3);
由于在C/C++中的浮点型常量默认是double类型,而double类型既可以给int型变量赋值,又可以给float型变量赋值
赋值兼容(隐式类型转换)所导致的函数重载二义性问题
→解决方法
int fun(double a){}
加入新的重载函数,使得类型有确定的调用方式,不存在赋值兼容
fun((int)2.3);
明确函数调用时的参数类型,可以使用强制类型转换
(7)作用域标识符::
通常情况下,如果有两个同名变量,一个是全局的,另一个是局部的,那么局部变量在其作用域内具有较高的优先权,它将屏蔽全局变量。
如果希望在局部变量的作用域内使用同名的全局变量,可以在该变量前加上“::
”,此时::value
代表全局变量value,“::
”称为作用域标识符。
#include <iostream>
using namespace std;
int value; //定义全局变量value
int main()
{
int value; //定义局部变量value
value = 100;
::value = 1000;
cout << "local value : " << value << endl;
cout << "global value : " << ::value << endl;
return 0;
}
(8)强制类型转换
可用强制类型转换将不同类型的数据进行转换。例如,要把一个整型数(int)转换为双精度型数(double),可使用如下的格式:
int i = 10;
double x = (double)i;
或
int i = 10;
double x = double(i);
12345
以上两种方法C++都能接受,建议使用后一种方法。
(9)new和delete运算符
程序运行时,计算机的内存被分为4个区:程序代码区、全局数据区、堆和栈。其中,堆可由用户分配和释放。C语言中使用函数malloc()
和free()
来进行动态内存管理。C++则提供了运算符new
和delete
来做同样的工作,而且后者比前者性能更优越,使用更灵活方便。
指针变量名 = new 类型
int *p;
p = new int;
delete 指针变量名
delete p;
12345
下面对new和delete的使用再做一下几点说明:
-
用运算符new分配的空间,使用结束后应该用也只能用delete显式地释放,否则这部分空间将不能回收而变成死空间。
-
在使用运算符new动态分配内存时,如果没有足够的内存满足分配要求,new将返回空指针(
NULL
)。 -
使用运算符new可以为数组动态分配内存空间,这时需要在类型后面加上数组大小。
指针变量名 = new 类型名[下标表达式]; int *p = new int[10]; 12
释放动态分配的数组存储区时,可使用delete运算符。
delete []指针变量名; delete p; 12
-
new 可在为简单变量分配空间的同时,进行初始化
指针变量名 = new 类型名(初值); int *p; p = new int(99); ··· delete p;
(10)引用
引用(reference
)是C++对C的一个重要扩充。变量的引用就是变量的别名,因此引用又称别名。
真实的数据类型 &引用名 = 已定义的变量名
引用与其所代表的变量共享同一内存单元,系统并不为引用另外分配存储空间。实际上,编译系统使引用和其代表的变量具有相同的地址。
-
引用并不是一种独立的数据类型,它必须与某一种类型的变量相联系。在声明引用时,必须立即对它进行初始化,不能声明完成后再赋值。
-
为引用提供的初始值,可以是一个变量或者另一个引用。
-
引用的类型必须和其所对应的变量的类型相同
-
对引用的操作和对引用对应的变量的操作是完全等价的
-
不允许建立void类型的引用
-
不能建立引用的数组
-
可以用const对引用加以限定,不允许改变该引用的值,但是它不阻止引用所代表的变量的值。
#include<iostream>
using namespace std;
int main() {
int a = 8;
int& ref_a = a;
ref_a++;
cout << a << endl;//9
}
//上面代码输出a和ref_a的值相同,地址也相同。
引用就是指针的语法糖(syntax sugar)
我们为什么要使用引用呢?我们干嘛要起个别名而不直接用真名呢?
假设我们想通过调用一个函数来实现一个整型变量的自增,有下面3种传递方式:
值传递:形参的改变无法改变实参的值
void fun(int x) {
x++;
}
int main() {
int a = 8;
fun(a);
cout << a << endl;//8
}
地址传递:形参的改变可以改变实参的值
void fun(int* x) {
(*x)++;//不加括号就会先自增再解引用
}
int main() {
int a = 8;
fun(&a);
cout << a << endl;//9
}
引用传递:形参的改变可以改变实参的值
void fun(int& x) {
x++;
}
int main() {
int a = 8;
fun(a);
cout << a << endl;//9
}
是不是比使用指针更简洁些呢
那么,可不可以给引用取别名呢?答案是可以的
int a = 8;
int& a1 = a;
int& a2 = a1;//给引用取别名(引用的嵌套)
int& a3 = a;//同一个对象或变量可以取多个别名
引用和指针的区别
- 指针是另一个变量,而引用是自己本身,指针会占用额外存储空间,但引用不会占用额外存储空间,也就是说别名和真名是同一个东西,共享同一块内存。
- 指针可以不用初始化赋值,但是引用必须初始化赋值
- 指针可以有多级指针,但是引用没有多级引用
- 指针可以改变指向,但是引用不能改变其所指代的变量或对象
- 指针是间接访问,引用是直接访问
另外,可以将引用的地址赋值给一个指针,此时指针指向的是原来的变量。
引用作为函数参数、使用引用返回函数值
#include <iostream>
using namespace std;
void swap(int &a, int &b)
{
int t = a;
a = b;
b = t;
}
int a[] = {1, 3, 5, 7, 9};
int& index(int i)
{
return a[i];
}
int main()
{
int a = 5, b = 10;
//交换数字a和b
swap(a, b);
cout << "a = " << a << " b = " << b << endl;
cout << index(2) << endl; //等价于输出元素a[2]的值
index(2) = 100; //等价于将a[2]的值赋为100;
cout << index(2) << endl;
return 0;
}
关于指针的更多内容:【C语言】指针
C++的一些标准(兼容旧标准)
1.以初始化列表的方式赋值
int c{2};
int d{(int)3.3};
int arr1[6]{1,2,3};
2.空指针
int *p=NULL;//旧标准
int *p1=nullptr;//新标准
3.自动类型
auto x=10.6;//根据初始化赋值的类型决定变量的类型
4.decltype的使用:可以理解为 复制类型
int n=123;
decltype(n) m=100;//定义一个和变量n一样类型的变量m
decltype((n)) k=n;//给变量n取一个别名k,类似于引用 int& k=n;
123
5.给数据类型取别名
typedef int HP;//旧
using MP = int;//新
typedef void(*pFun)();//旧
using PFun = void(*)();//新
typedef char str[10];//旧
using Str = char[10];//新
123456
6.新的for循环语法规则
#include<iostream>
int main() {
int arr2[10] = { 1,2,3,4,5,6,7,8,9,0 };
for (int i = 0; i < 10; i++) {
std::cout << arr2[i] << '\t';
}
std::cout<<std::endl;
//新
for (auto i : arr2) {
std::cout << i << '\t';
}
}
这个方法只能用来遍历数组或者容器,i所代表的是里面存储的数据元素。指针不能这样遍历。
好啦,下面我们就进入C++的系统学习吧!(笔者会陆续更新哦)
三大特点:
①封装——类和对象
②继承与派生
③多态性与虚函数
运算符重载
函数模版与类模版
C++的输入和输出
异常处理和命名空间
STL标准模板库