c++语法基础
- 基础数据类型与表达式
- 数据的输出与输入
类
- 类和对象的关系
- 类的声明
- 成员函数
- 类的定义和使用
- 构造函数与析构函数
- 类的组合
数据的共享与保护
- 类的友元
- 常对象与常引用
- 对象的常数据成员/成员函数
继承和派生
- 继承和派生的定义与构成
- 派生类的构造函数和析构函数
- 构造函数链与析构函数链
- 派生类对基类成员的访问控制
- 派生类和基类的兼容规则
- 多继承的声明
多态性
- 运算符的重载
- 虚函数与虚函数的虚析构函数
- 纯虚函数与抽象类
文件流
异常处理
1.1 基础数据类型与表达式
c++与c语言相比数据类型比c语言强,因为c++很多类型都是开发者已经设计好了的类模板,因此通用性更好,在学习c++的时候,我学习了stl标准模板库 dequeue ,map,stack,vector,string,set这些应该是常用的,bool类型和string类型是以前学c语言不曾接触过的,string特别好用,开发者已经把所有的功能都完善了。
1.2数据的输出与输入
c++的输入输出称为流输入,流输出,关键字:cout,cin包含在文件库中,与c语言不同的是,程序员不需要制定类型输入输出,c++自动识别,c++的i/o格式控制有
操作符名 | 含义 |
---|---|
dec | 数据采用10进制表示 |
hex | 数据采用16进制表示 |
oct | 数据采用8进制表示 |
ws | 提取空白符 |
endl | 插入换行符,并刷新流 |
ends | 插入空字符 |
setprecision(int) | 设置浮点数的小数位数(包括小数点) |
setw(int) | 设置域宽 |
left | 左对齐 |
right | 右对齐 |
showpoint | 总是打印小数点后尾随0 |
fixed | 固定小数点位置的形式表示浮点数 |
fixed常与setprecision连用。
2.1类和对象的关系
类的声明:
class 类名{
private:
数据成员和成员函数;
public:
数据成员和成员函数;
protected:
数据成员和成员函数;
}
函数对各成员数据的调用权限:
函数 | 私有成员 |
---|---|
类中函数 | 可以调用 |
类外函数 | 不可调用 |
函数 | 公有成员 |
---|---|
类中函数 | 可以调用 |
类外函数 | 可以调用 |
函数 | 保护成员 |
---|---|
类中函数 | 可以调用 |
类外函数 | 不以调用 |
成员函数的定义:
返回值类型 类名:成员函数名(参数表){
函数体;
}
例
void Point::setPoint(int x,int y){
xpos=x;ypos=y;
}`
2.5类的构造函数与析构函数
当定义类对象的时候,构造函数会自己执行,当我们没有写构造函数的时候,系统会自动生成一个默认构造函数,所有类对象在定义的时候都必须调用构造函数,不存在没有构造函数的对象,当我们自己定义了构造函数的时候,系统则不会自动生产构造函数。
扯一下一次性对象,例:cout<<Data(2003,12,23);Data(2003,12,23)是一个对象,该对象在做了<<操作后便烟消云散了,所以这种对象一般用在创建后不需要反复使用的场合;
复制构造函数:类名:复制构造函数(类名 &引用名)或者类名:复制构造函数(const 类名&引用名)
如果程序员没有定义复制构造函数,则编译器自己生成一个隐含的拷贝构造函数,这个函数执行的功能是:用作为初始值的对象的每个数据成员的值,初始化将要建立的对象的对应数据成员。但是,当类中的数据成员中使用new运算符,动态申请存储空间进行赋值时,必须在类中显示定义一个完成复制功能的构造函数,一变正确实现数据成员的复制。
什么时候复制构造函数会被调用呢?
1.当用类的一个对象去初始化该类的另一个对象的时候,系统自动调用拷贝构造函数,实现赋值。
2.若函数的形参是类对象,调用函数时,实参传递形参,系统自动调用拷贝构造函数。
3.当函数的返回值是类对象时,系统自动调用拷贝构造函数。
析构函数:
声明:类名::~析构函数名(){语句;}
析构函数是在对象生存期即将结束的时候由系统自动调用。
2.6类的组合
在类中定义的数据成员一般都是基本数据类型或服复合数据类型。但是还可以根据需要使用
其他类的对象作为正在声明的数据成员。复杂的对象还可以有比较简单的对象一某种方式组合
而成,复杂对象和组成它的简单对象之间的关系是组合关系。
在一个类中内嵌另一个类的对象作为数据成员,称为类的组合。该内嵌对象称为对象成员,也称为
子对象。例如:
class A
{
.......
};
class B
{
A a; //类A的对象a为类B的对象成员
public:
....
};
- 数据的共享与保护
3.1类的友元
友元函数是能够访问私有成员的非成员函数;
直接写一个例子吧:
class point{
public:
point(double x,double y):x(x),y(y){}//只有初始化列表才能这样赋值,并且不会产生二义性
private:
double x,y;
friend double distance(point &a,point &b);//友元函数的声明,distance函数可以直接访问私有成员了;
}
double distance (point &a,point &b)
{
double dx=a.x-b.x;
double dy=a.y-b.y;
return sqrt(dx*dx+dy*dy);
}
3.2常对象与常引用
常引用:被引用的对象不能被更新
const 类型 &引用名
常对象:必须进行初始化,不能被更新
cosnt 类名 对象名
常对象的数据成员值在对象的整个生存期间内不能被改变,常对象必须进行初始化,而且不能被更新。
这个我感觉还挺简单的,主要是下面的有一点点混淆;
3.3对象的常数据成员/成员函数
常成员函数是指有const修饰符修饰的成员函数,在常成员函数中不得修改类中的任何数据成员的值。
常成员函数的声明形式:
类型 函数名(参数表)const;
void point::output()const;
在point函数体中类的数据成员不能被修改但是可以被访问;
说明:
- const是函数类型的一个组成部分,因此在函数定义部分要用const关键字修饰;
- 常成员函数不能更新对象的数据成员,也不能调用该类中非const成员函数;
- 常成员函数可以使用非const数据成员;
- 常对象只能调用常成员函数;
- const可以用于区分重载函数;
常数据成员:
声明:const 类型名 变量名;
注意:
- 使用const说明的数据成员;
- 任何函数都不能修改改数据成员的值;
- 其初始化由构造函数完成,且只能在初始化列表中给出;//这个我好像以前都没注意AI
- 静态常数据成员还是在文件作用域内定义性说明和初始化;
常引用
常引用所引用的对象不被更新
声明:const 类型说明符 &引用名或者 类型说明符 const &引用名;
举例:`void display(const double &r)//const保护数据不被修改,&引用提高效率;
`
4. 继承和派生
后面的内容感觉比较简单就不写那么详细了;
派生类的定义:
class 派生类名 :派生方式 基类名{
//派生类成员说明
}
额不知道怎么描述,在调用派生类的构造函数的时候,会先隐式调用基类的构造函数,这算是一个考点吧;
例如:circle(){}
实际上是这样的circle:shaple(){}; 其中circle是派生类 shaple是基类
4.3构造函数链与析构函数链
这个也比较简单,就是在调用派生类构造函数的时候先调用基类的构造函数,析构函数则是相反的顺序,类似于进栈出栈的操作;
4.4派生类对基类成员的访问控制
- 引言:因为继承和派生的发生,派生类中的成员可以分为两部分,一部分是从基类继承来的成员,还有一部分是派生类新增的成员,这时就产生了与访问控制有关的问题,事实上,在继承和派生类发生时,派生类不是简单的把积累所有的成员变成自己的成员,然后随意的访问;
- c++中类的派生方式有三种:public、private、protected。不同的派生方式决定了基类成员在派生类中的访问权限,对于从基类中继承来的成员,这个访问来自两个方面:
- 派生类中的新增成员访问他们
- 继承树无关的其他类或者函数,通过派生类的对象访问他们;
4.5派生类和基类的兼容规则
这个兼容规则还挺重要的,上机的时候对这个不太了解走了不少弯路;
派生类和基类的兼容规则,是指在任何需要基类对象的地方,都可以使用派生类的对象来代替,前提是公有派生。
这是因为在这三种派生方式中,只有公有派生使得派生出的派生类完整地保留了基类的特征。
在派生类和基类的兼容规则中,所说的替代主要包含以下三种情况:
- 派生类的对象可以赋值给基类对象
1.1假设有如下对象定义:
circle cir;
shape sha;
sha=cir;
这种赋值是合法的,不过派生类的新增成员相当于舍弃赋给基类了,因为基类没有派生类成员;
- 派生类的对象可以初始化基类的引用
3.2Circle circle; shape &shaperef=circle;
这个赋值是合法的,和上面的差不多就不多讲了,但是
shape shape;
Circle &circleref=shape;
这个是非法的,因为circleref的类型是派生类的引用,派生类的对象占据的内容往往比基类大,这样赋值就会存在安全隐患,所以这种引用赋值是非法的;
- 派生类的指针可以赋值给基类的指针
5.1Circle circle; Shape *pShape=&circle;
与引用的情况类似,以上对基类指针pShape的初始化是合法的。但是
Shape shape;
Circle *pCircle=&shape;
这个是非法的;
总结在引用赋值的时候,派生类赋值给基类是允许的,而基类赋值给派生类是禁止的;
4.6多继承的声明
顾名思义,多继承就是多继承几个基类;
声明:
class 派生类名:派生方式1 基类名1,派生方式2 基类名2....
{
//派生类成员说明
}
5.1运算符的重载
运算符的重载分为{全局函数的重载,成员函数的重载};
全局:
例如在对复数+操作时,可以这样重载
complex operator+(const complex &c1,const complex &c2)
{
complex c;
c.setreal(c1.getreal()+c2.getreal());
c.setimag(c1.getimg()+c2.getimg());
return c;
}
上述代码实在是。。。。惨目忍睹,可以直接将该函数声明为complex类的友元,可以减少很多码量。
根据上面代码可以很容易想到全局函数运算符重载的声明:
返回值类型 operator运算符(形参列表)
{
//函数体
}
这里我也直接给出成员函数的重载了:
返回值类型 类名::operator 运算符(形参列表)
{
//函数体
}
好像也差不多多哈,我下面给出几个运算符重载模板
ostream& operator<<(ostream &os,const 类型名&a)
{
os<<a.son<<"/"<<a.parent<<endl;
return os;
}
friend ostream & operator<<(ostream &o,const Complex &A);
friend istream & operator>>(istream &o,Complex &A);//重载输入操作符的时候不要使用const
因为流输入输出好像没太多拓展性,就那里看了,注意声明为友元。
5.2虚函数与虚函数的虚析构函数
多态可以简单地理解成1条函数调用语句能吊用不同的函数,或者理解成对不同对象发送同一消息,使得不同对象各有各不同的行为。c++的虚函数是用来解决多态问题的,所谓虚函数,指在声明时前面加了一个virtual关键字的成员函数,以上就是在基类中声明的这个函数是虚拟的,并不是实际存在的,之后派生类中才正式定义这个函数。
代码就不贴了,我懂就行。
例子:
class c
{
public:
virtual string tostring()
{
return "class c";
}
}
注意:
- 在基类中,函数必须声明为虚函数。
- 在虚函数中,引用对象的变量必须以引用或者指针的形式传递。
例如:
void dispaly(c *p)//指针传递
{
cout<<p->tostring()<<endl;
}
虚析构函数
在c++中,构造函数不能被声明为虚成员函数,这是因为在执行构造函数时类的对象还未完成建立过程,当然谈不上把函数与类的对象进行绑定。建立一个派生类对象时,必须从类层次的跟开始,沿着继承路径逐个调用基类的构造函数。但是,析构函数可以是虚函数成员,虚析构函数用于指引delete运算符正确析构动态对象。
举个例子说明虚析构函数的重要性:
class A
{
private:
char *p;
public:
A()
{
p=new char[1000];
}
~A()
{
delete []p;
}
}
在这里因为空间是动态分配的,如果没有虚析构函数,默认构造函数是删除不掉这块空间的。
纯虚函数
声明:
virtual 返回值类型 getarea()=0;
纯虚函数是为了更好的使用多态性,人们常常需要在基类中定义虚函数,在有些情况下,基类本身对象是不合情理的。例如,我们要设计一个能够细分为矩形,三角形,圆形和椭圆形的图像类,图形类的派生函数都能计算面积,因此最好在图形类这一基类中定义一个虚函数(无实际应用)。
有纯虚函数的函数我们称为抽象类。
- 文件流
文件流比较简单但是却是非常实用的,当你在比赛常上面提交代码,可能它的输入样例特别多,而你不能确定一次过,需要多次调整,这个时候文件流的作用就体现的淋漓尽致了。
这里是引用
- 异常处理
#include "pch.h"
#include "pch.h"
#include<fstream>
#include <iostream>
#include <string>
#include<sstream>
using namespace std;
string a;
int b[105];
int x[15];
int y[15];
int main()
{
int n;
cin >> n;
int b;
for (int i = 1; i <= n; i++)
{
b = 0;
try {
cin >> a;
b = atoi(a.c_str());
if (b > 10 || b < 0)
{
b = 0;
throw 1.0;
}
for (int i = 0; i < a.length(); i++)
{
if (isalpha(a[i]))
{
b = 0;
throw 1;
}
}
}
catch (double a)
{
cout << "The number must between 1-10. Try again." << endl;
}
catch (int a)
{
cout << "Please enter your number using digits only. Try again." << endl;
}
x[i] = b;
}
for (int i = 1; i <= 10; i++)
{
if (x[i])
++y[x[i]];
}
for (int i = 1; i <= 10; i++)
{
cout << i << ":";
while (y[i])
{
cout << "*";
y[i]--;
}
cout << endl;
}
}
这是我们的实训作业,将数字1-9出现的次数转化为柱形图,当数字>10||<0提示错误,当数字为英文提示错误,这里都是用的异常处理。
异常处理其实比较简单吧,和多加一个if else没什么区别。
上述代码中,当输入数字不在有效范围 throw 1.0;然后程序马上catch (double a),提示输出,当输入数字是英文throw 1;然后程序 catch (int a),catch(这里面是根据你抛出的类型来决定的)。
异常处理基本语法:
C++ 通过 throw 语句和 try...catch 语句实现对异常的处理。throw 语句的语法如下:
throw 表达式;
该语句拋出一个异常。异常是一个表达式,其值的类型可以是基本类型,也可以是类。
try...catch 语句的语法如下:
try {
语句组
}
catch(异常类型) {
异常处理代码
}
...
catch(异常类型) {
异常处理代码
}
ok以上就是全部内容了,其实偷了一点懒,stl都没学,模板写没写,算了有一点点不想写了,就算过了一遍书了吧。