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:
       ....
};
  1. 数据的共享与保护
    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函数体中类的数据成员不能被修改但是可以被访问;
说明:

  1. const是函数类型的一个组成部分,因此在函数定义部分要用const关键字修饰;
  2. 常成员函数不能更新对象的数据成员,也不能调用该类中非const成员函数;
  3. 常成员函数可以使用非const数据成员;
  4. 常对象只能调用常成员函数;
  5. const可以用于区分重载函数;

常数据成员:
声明:const 类型名 变量名;
注意:

  1. 使用const说明的数据成员;
  2. 任何函数都不能修改改数据成员的值;
  3. 其初始化由构造函数完成,且只能在初始化列表中给出;//这个我好像以前都没注意AI
  4. 静态常数据成员还是在文件作用域内定义性说明和初始化;

常引用
常引用所引用的对象不被更新
声明:const 类型说明符 &引用名或者 类型说明符 const &引用名;
举例:`void display(const double &r)//const保护数据不被修改,&引用提高效率;

`
4. 继承和派生
后面的内容感觉比较简单就不写那么详细了;
派生类的定义:

class 派生类名 :派生方式 基类名{
        //派生类成员说明
}

额不知道怎么描述,在调用派生类的构造函数的时候,会先隐式调用基类的构造函数,这算是一个考点吧;
例如:circle(){}实际上是这样的circle:shaple(){}; 其中circle是派生类 shaple是基类

4.3构造函数链与析构函数链
这个也比较简单,就是在调用派生类构造函数的时候先调用基类的构造函数,析构函数则是相反的顺序,类似于进栈出栈的操作;

4.4派生类对基类成员的访问控制

  1. 引言:因为继承和派生的发生,派生类中的成员可以分为两部分,一部分是从基类继承来的成员,还有一部分是派生类新增的成员,这时就产生了与访问控制有关的问题,事实上,在继承和派生类发生时,派生类不是简单的把积累所有的成员变成自己的成员,然后随意的访问;
  2. c++中类的派生方式有三种:public、private、protected。不同的派生方式决定了基类成员在派生类中的访问权限,对于从基类中继承来的成员,这个访问来自两个方面:
  3. 派生类中的新增成员访问他们
  4. 继承树无关的其他类或者函数,通过派生类的对象访问他们;

4.5派生类和基类的兼容规则
这个兼容规则还挺重要的,上机的时候对这个不太了解走了不少弯路;
派生类和基类的兼容规则,是指在任何需要基类对象的地方,都可以使用派生类的对象来代替,前提是公有派生。
这是因为在这三种派生方式中,只有公有派生使得派生出的派生类完整地保留了基类的特征。
在派生类和基类的兼容规则中,所说的替代主要包含以下三种情况:

  1. 派生类的对象可以赋值给基类对象
    1.1假设有如下对象定义:
 circle cir;
 shape sha;
 sha=cir;

这种赋值是合法的,不过派生类的新增成员相当于舍弃赋给基类了,因为基类没有派生类成员;

  1. 派生类的对象可以初始化基类的引用
    3.2 Circle circle; shape &shaperef=circle;
    这个赋值是合法的,和上面的差不多就不多讲了,但是
shape shape;
Circle &circleref=shape;

这个是非法的,因为circleref的类型是派生类的引用,派生类的对象占据的内容往往比基类大,这样赋值就会存在安全隐患,所以这种引用赋值是非法的;

  1. 派生类的指针可以赋值给基类的指针
    5.1 Circle 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";
       }
}

注意:

  1. 在基类中,函数必须声明为虚函数。
  2. 在虚函数中,引用对象的变量必须以引用或者指针的形式传递。
    例如:
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;

纯虚函数是为了更好的使用多态性,人们常常需要在基类中定义虚函数,在有些情况下,基类本身对象是不合情理的。例如,我们要设计一个能够细分为矩形,三角形,圆形和椭圆形的图像类,图形类的派生函数都能计算面积,因此最好在图形类这一基类中定义一个虚函数(无实际应用)。
有纯虚函数的函数我们称为抽象类。

  1. 文件流
    文件流比较简单但是却是非常实用的,当你在比赛常上面提交代码,可能它的输入样例特别多,而你不能确定一次过,需要多次调整,这个时候文件流的作用就体现的淋漓尽致了。

这里是引用

  1. 异常处理
#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都没学,模板写没写,算了有一点点不想写了,就算过了一遍书了吧。