前言
C++是C语言的扩展,两种语言完全兼容,C语言的绝大部分内容可以直接用于C++的程序设计,用C语言编写的程序可以不加修改地用于C++。本文章适用于学完c语言想继续学习c++的uu们。
本文章只适合快速入门,知识点讲解并不算深入。看完之后能写和看懂基本的代码。
1.头文件
C++的头文件不必是.h结尾。
例如C语言中的<math.h>在C++中被命名为<cmath>
2.名字空间
C++中为了防止名字冲突(出现同名),引入了名字空间(namespace)
有三种使用名字空间的方法(X是名字空间名):
(1)using namespcae X; 引入整个名字空间。后面如果使用于first中同名的变量,默认变量类型与first中定义的一致。如果引入多个名字空间,必须保证引入的名字空间中没有同名的变量,否则编译器报错。
(2)using X::name(变量名) 只引入一个变量名
(3)X::name 使用X中的一个叫name的变量
看下面这个使用名字空间的例子:
#include<iostream>
namespace first //定义名字空间first
{
int a;
int b;
}
namespace second
{
double a;
double b;
double c;
double d;
}
using namespace first; //引入整个名字空间
using second::c; //只引入second中的变量c
using namespace std; //引入标准库名字空间(每个c++程序中必出现)
int main()
{
second::a = 5; //使用second中的变量a
a=4;
cout<<a;
return 0;
}
上述程序的执行结果为4。能够证明,当引用了一个名字空间之后,后面如果使用与名字空间中同名的变量,默认变量类型与名字空间中定义的一致。
3.输入输出
标准输入输出
在C语言中,我们通常会使用 scanf、printf 和其他所有标准C输入和输出函数,来对数据进行输入输出操作。在C++语言中,C语言的这一套输入输出库我们仍然能使用,但是 C++ 又增加了一套新的、更容易使用的输入输出库,即iostream库,头文件<iostream>。
C++把输入和输出看作一个“流”,并且用输出运算符 << 和输入运算符 >> 对数据进行输入输出。
一个流就是一个字符序列,是从IO设备读出或写入IO设备的。术语“流”(stream)想要表达的是,随着时间的推移,字符是顺序生成或消耗的。
标准库中的名字都属于名字空间std。因此,不仅要包含标准库的头文件,还要包含标准库的名字空间才能正常使用标准库的函数。
#include<iostream>
using namespace std; //引入标准库名字空间
int main()
{
int a;
cout<<"从键盘上输入一个数"<<endl;
cin>>a;
cout<<a;
return 0;
}
注意,不管是 cin 也好,cout 也好,<< 和 >> 一次性只能输出或者输入一个,例如以下的写法是错误的:
cout << a, b, c;
cin >> a, b, c;
4.引用
C++中引入了“引用类型”,即一个变量是另一个变量的别名。
int &a = b;
a = 2;
上面的代码中,&符号是引用类型的标志。通过改变a的值,就能改变b的值。即变量a是变量b的一个别名。
由于这种特性,引用类型常常用于函数传参。当你需要保存改变后的参数的值的时候,使用引用类型传参。类似于指针。例如下面的这个函数定义
void swap(int &a,int &b)
5.函数的默认参数
函数的形参可以有默认值。但是有默认值的形参必须放在最右边。
int tset(int a,int b,int c=7,int d=2)
6.try-catch处理异常情况
正常代码放在try块,catch捕获try中抛出的异常。
#include<iostream>
#include<string>
using namespace std;
int main()
{
int a;
cin>>a;
try
{
if(a>100) throw "too big";
if(a<10) throw 10;
}
catch(char const* s)
{
cout<<"错误原因是:"<<s<<endl;
}
catch(int result)
{
cout<<result<<endl;
}
catch(...)
{
cout<<"其他异常情况在这里捕获"<<endl;
}
cout<<a;
return 0;
}
当抛出的异常为不同的数据类型时,可以使用多个catch捕获对应类型的异常。
7.模板函数
设想一种情况,你想写一个函数,返回两个数中较小的那个数。但是实际应用的时候,想要比较的两个数也许是整型,也许是浮点数,也许一个是整型一个是浮点数。面对这种情况,在c语言中只能写多个函数。为了解决这种不方便的情况,C++中引入了模板函数。
使用的语法如下:
(1)传入的参数类型相同
#include<iostream>
using namespace std;
template<class T> //这里的T可以替换成其他符号,作为函数传参和返回参数的类型,
//程序运行时会自动确定类型
T mymin(T a,T b)
{
if(a>b) return b;
else return a;
}
int main()
{
double a=10;
double b=5.5;
printf("%lf",mymin(a,b));
return 0;
}
(2)传入的参数类型有不同的
#include<iostream>
using namespace std;
template<class T1,class T2> //这里的T可以替换成其他符号,作为函数传参和返回参数的类型,程序运行时会自动确定类型
T1 mymin(T1 a,T2 b)
{
if(a>b) return T1(b);
else return a;
}
int main()
{
int a=10;
double b=5.5;
printf("%d",mymin(a,b));
return 0;
}
8.类
(1)什么是类
类是属于用户自定义的数据类型, 并且该类型的数据具有一定的行为能力, 也就是类中说描述的方法。通常来说, 一个类的定义包含两部分的内容, 一是该类的属性, 另一部分是它所拥有的方法。以 "人类" 这个类来说, 每个人都有自己的姓名、年龄、出生日期、体重等, 为 人类 的属性部分, 此外, 人能够吃饭、睡觉、行走、说话等属于人类所具有的行为。
(2)C++类的定义
C++中使用关键字 class 来定义类, 其基本形式如下:
class 类名
{
public:
//公共的行为或属性
private:
//私有的行为或属性
};
说明:
①. 类名 需要遵循一般的命名规则;
②. public 与 private 为属性/方法限制的关键字, private 表示该部分内容是私密的, 不能被外部所访问或调用, 只能被本类内部访问; 而 public 表示公开的属性和方法, 外界可以直接访问或者调用。(下面的例子会进一步解释)
一般来说类的属性成员都应设置为private, public只留给那些被外界用来调用的函数接口, 但这并非是强制规定, 可以根据需要进行调整;
③. 结束部分的分号不能省略。
(3)C++类的实现
在上面的定义示例中我们只是定义了这个类的一些属性和方法声明, 并没有去实现它, 类的实现就是完成其方法的过程。类的实现有两种方式, 一种是在类定义时完成对成员函数的定义, 另一种是在类定义的外部进行完成。
①.在类定义时完成对成员函数的定义
在类中定义成员函数时, 编译器默认会争取将其定义为 inline 型函数。
#include<iostream>
using namespace std;
class Point //定义一个描述点的类
{
public:
void setPoint(int x,innt y) //定义一个设定点的横纵坐标的方法
{
xPos = x;
yPos = y;
}
void printPoint() //定义一个打印点的横纵坐标的方法
{
cout<<"x="<<xPos<<endl;
cout<<"y="<<yPos<<endl;
}
private:
int xPos;
int yPos;
};
int main()
{
Point M;
M.setPoint(10,20);
M.printPoint();
return 0;
}
上面的类中,xPos和yPos是私有的,两个函数是公共的。所以在主函数中,可以通过M.setPoint来调用类中的函数。但是下面的写法是错误的:
M.xPos = 10
M.yPos = 20
②. 在类外定义成员函数
在类外定义成员函数通过在类内进行声明, 然后在类外通过作用域操作符 :: 进行实现, 形式如下:
返回类型 类名::成员函数名(参数列表)
{
//函数体
}
#include <iostream>
using namespace std;
class Point
{
public:
void setPoint(int x, int y); //在类内对成员函数进行声明
void printPoint();
private:
int xPos;
int yPos;
};
void Point::setPoint(int x, int y) //通过作用域操作符 '::' 实现setPoint函数
{
xPos = x;
yPos = y;
}
void Point::printPoint() //实现printPoint函数
{
cout<< "x = " << xPos << endl;
cout<< "y = " << yPos << endl;
}
int main()
{
Point M; //用定义好的类创建一个对象 点M
M.setPoint(10, 20); //设置 M点 的x,y值
M.printPoint(); //输出 M点 的信息
return 0;
}
(4)对象的作用域、可见域与生存周期
类对象的作用域、可见域以及生存周期与普通变量的保持相同, 当对象生存周期结束时对象被自动撤销, 所占用的内存被回收, 需要注意的是, 如果对象的成员函数中有使用 new 或者 malloc 申请的动态内存程序不会对其进行释放, 需要我们手动进行清理, 否则会造成内存泄露。
9.函数和运算符重载
(1)函数重载
C++允许有函数重名,调用时依据参数类型和参数个数进行区分。
注意:不根据返回类型区分
同一个程序中出现以下三个函数的定义是可以的。
int my_min(int a,int b)
int my_min(int a,double b)
int my_min(int a,int b,int c)
(2)运算符重载
①什么是运算符重载
运算符重载,就是让原本已经存在的运算符有了新的用法和意义。
比如我们熟知的减号(-),原本是用来进行数字的相减处理。但经过运算符重载后,它可以用来进行其他类型的相减,像时间相减、日期相减、字符相减等等。只要是你能想到的,通过运算符重载基本都能够实现。
对于C++而言,运算符重载一般是作为类的成员函数出现。因为当我们需要运算符重载时,往往是类中一种特殊的类型需要处理或者类本身需要处理。就像我们可能会把时间作为一个类,里面有小时、分钟、秒。如果让时间相减,那么减号的参数类型就变成了时间类。因此,重载最好作为类的成员函数出现,减少在我们调用运算符重载时发生冲突的可能。
②运算符重载的形式
[返回值] operator[运算符] (参数...) { ... };
这里我们必须注意的是,重载的参数个数必须与运算符原意的个数一致。比如+号的参数就是左加数和右加数两个。那么当我们重载加号时也要保证有左右两个加数作为参数。
重载作为普通函数时:
单目:右操作数是形参;双目:左边形参作为运算符左操作数,右边形参是右操作数。
重载作为类的成员函数时:
如果运算符参数只有一个,那么不需要写参数;如果运算符参数有两个,那么只需要写一个参数。
因为类的成员函数默认有一个this指针。它会直接指向类本身。换句话说,当我们写出运算符重载时,有一个参数就已经被this指针包含了。
单目运算符,this所指向运算符右参数,因为单目运算符的参数一般都在右边。
双目运算符,this指向运算符左参数。
以减号为例,两个时间类a和b相减时。如果是a - b,那么this指针指向a,反之则指向b。在声明函数时,我们只需要写右参数即可。a - b的话只需要写 int operator-(Time b);
当然,单目运算符由于只有一个参数,且该参数被this所指向,那么我们无需声明任何参数即可。
③应用--时间的加减
class Time
{
int _hour;
int _min;
int _sec;
public:
Time(int hour = 0, int min = 0, int sec = 0)
{
_hour = hour;
_min = min;
_sec = sec;
}
void Print()
{
cout << _hour << ":" << _min << ":" << _sec << endl;
}
Time operator+(int min)//加分钟
{
Time t(*this);//因为是+号,规定不能改变左右参数的值,所以使用t来取和用以返回。
t._min += min;
if (t._min > 59)//检查时间正确性
{
t._hour += (t._min / 60);
if (t._hour > 23)
t._hour /= 24;
t._min %= 60;
}
return t;
}
};
int main()
{
Time a(10, 30, 30);
(a + 140).Print();
return 0;
}