c++编程基础知识

c++编程基础知识,第1张

c++编程基础知识

目录

面向对象和面向过程:

分文件编程:

类:

类的成员函数

成员函数不占用存储空间

类成员的访问;

类是一种数据类型:

构造函数和析构函数:

构造函数的作用:

构造函数什么时候调用:

构造函数能调用吗:

构造函数重载(构造函数种类):

默认构造函数:

复制构造函数:

转换构造函数:

类型转换(返回)函数:

析构函数的作用:

析构函数的特点:

析构函数什么时候执行:

this指针

常成员对象

类对象之间的赋值:

对象的复制(克隆):

 类的静态数据成员

继承和派生:

三种继承方式:

派生类数据成员和基类数据成员重复情况:

继承的数据成员如何使用:

继承后端访问属性:

基类的构造函数的初始化:

基类和派生类构造函数和析构函数执行的先后顺序:

类中的数据成员是一个类:

多层派生的构造函数:

基类和子对象构造函数重载:

同时继承多个基类:

虚基类

为什么需要虚基类:

派生类对直接虚基类和原始基类的初始化:

派生类给基类赋值:

1,派生类对象向基类对象赋值:

2,派生类对象给基类引用对象赋值:

3,派生类对象给基类指针对象赋值:

如何在一个中调用另一个类的数据成员和函数:

类的组合

友元:

友元函数:

哪些函数可以成为友元函数:

友元函数的访问权限:

友元函数的调用:

友元类:

类对象数组:

如何给对象数组赋值:

参数默认值:

string:

string的数值转换:

命名空间

多态:

虚函数:

如何让实现同一接口:

实现:

如果把派生类中的”相同“函数声明为虚函数呢:

虚函数如何实现多态性:

虚析构函数:

纯虚函数:

抽象(基)类:

重载函数:

运算符重载:

为什么程序员定义一个运算符的新功能是运算符的重载:

为什么需要运算符重载(什么时候需要运算符重载函数):

运算符重载实现形式:

运算符重载规则:

运算符作为类成员函数和友元函数的区别:

重载运算符作为友元如何引用:

运算符重载函数的调用:

单目运算符重载:

自增,自减运算符重载:

>运算符重载:"><<和>>运算符重载:

cin,cout:

c和c++强制类型转换:

流:


面向对象和面向过程:

面向过程:为了实现某个目标 设计者需要自己一步一步的去完成,面向过程适合于小型项目的运用

面向对象:为了实现某个目标,设计者可以把这个目标划分为多个模块,然后设计一个或者多个对象去完成每一个对象自己的功能模块,以完成目标,面向对象适合于大型项目的实现

对象=数据+函数
注意区分变量和对象

分文件编程:

头文件:包含函数,类及数据成员声明,结构体的数据成员声明。

源文件:源文件要包含头文件,用来定义头文件中声明的东西(使用::)。

源文件包含的头文件,头文件可以不用包含。

但是头文件需要用到的一些命名空间(例如:std),头文件也需要直接引入

分文件编程如果找不到实现文件,就所有源文件一起编译

报错:undefined reference to `animal::~animal()'

 g++ student.cpp animal.cpp -o student.out

类:
  类的成员函数:

类的成员函数在类内定义和在类外定义的区别:

调用一个函数的时间开销远远大于小规模函数体中全部语句的执时间。为了减少时间开销,如果在类体中定义的成员函数中不包括循环等控制结构,c++系统会自动地对它们作为内置(inline)函数来处理。也就是说在程序调用这些函数时,并不是真正地执行函数的调用过程,而是把函数代嵌入程序的调用点。这样可以大大减少调用成员函数的时间开销

应该注意的是:如果成员函数不在类体内定义,而在类体外定义,系统并不把它期为内置函数,调用这些成员函数的过程和调用一般函数的过程是相同的。如果想将这些成员函数指定为内置函数,应当用inline作显式声明

值得注意的是:如果在类体外定义inline函数,则必须将类和成员函数的定义都放在同一个头文件中(或者写在同一个源文件中),否则编译时无法进行置换

只有在类外定义的成员函数规模很小而调用频率较高时,才指定为内置函数

成员函数不占用存储空间

不论成员函数在类内定义还是在类外定义,成员函数的代码段的存储方式是相同的,都不占用对象的存储空间。不要误以为在类内定义的成员函数的代码段占用对象的存储空间,而在类外定义的成员函数的代码段不占用对象的存储空间。

类成员的访问;

1,用成员运算符(点).

2,用指向类或者类对象的指针

3,引用

类是一种数据类型:

类和结构体都是数据类型,不占存储空间,只有实体对象才占用存储空间

构造函数和析构函数: 构造函数的作用:

实现数据成员初始化

构造函数什么时候调用:

建立类对象时

构造函数能调用吗:

不能

构造函数重载(构造函数种类):

用户定义有什么构造函数就只有什么构造函数,不会有默认构造函数,定义了什么构造函数,建立对象时就要有什么样的初始值,除非有多个构造函数

默认构造函数:

就是无参构造函数,用户不定义构造函数时,C++系统自动生成空(默认)构造函数

复制构造函数: 转换构造函数:

实质,利用一个对象或者标准数据类型给当前对象赋值

eg: 

class A;

A a1(3),a2;

a2=A(4);//建立一个无名对象,并初始化为其成员变量为4,然后把这个无名对象赋给a2

转换构造函数只能有一个参数

可能用到:

Teacher(student& s)

{

    strcmp(name,s.name);

    age=s.age;

}

类型转换(返回)函数:

形式:

operator 要返回的类型()

{}

注意:operator前无返回类型,返回类型紧跟()前。

类型转换函数只能作为类成员,不能作为友元函数或者普通函数

和转换构造函数相反,类型转换函数是在类中定义一个函数,用来返回一个指定类型的数据,为什么要返回一个指定类型数据;

class A;

A a;

int n=10;

int b;

b=n+a;//b=a+n;

编译器首先会先寻找有没有重载函数,如果没有匹配的重载函数,再到类中中寻找一个返回int类型数据的函数,如果有就执行类型返回函数返回一个int数据和n相加,如果都没有就报错。

class add{};

int main()
{
        add num1;
        cin>>num1;
        cout<

        int h=10,f;
        f=h+num1;
        cout<         f=num1+20;
        cout<         return 0;
}
利用类型转换函数可以实现类和普通函数相加

析构函数的作用:

对类中的数据进行清理

析构函数的特点:

三无:无返回值,无类型,无参数

不能重载

析构函数什么时候执行:

析构函数存在的周期如同局部变量:

(1)如果在全局范围中定义对象(即在所有函数之外定义的对象),那么它的的构造函数在本文件模块中的所有函数(包括main函数)执行之前调用(全局变量,对象最先建立)。但如果一个程序多个文件,而在不同的文件中都定义了全局对象,则这些对象的构造函数的执行顺序是不确定的,当main函数执行完毕或调用exit函数时(此时程序终止),调用析构函数。
(2)如果定义的是局部自动对象(例如在函数中定义对象),则在建立对象时调用其构造函数。如果对象所在的函数被多次调用,则在每次建立对象时都要调用构造函数。函数调用结束、对象释放时,先调用析构函数。
3)如果在函数中定义静态(static )局部对象,则只在程序第1次调用此函数>1用构造函数一次,在调用函数结束时对象并不释放,因此也不调用析构函数。main函数结束或调用exit结束程序时,才调用析构函数
 

this指针

在每一个成员函数中都包含一个特殊的指针,是指向本类对象的指针,它的值是当前被调用的成员函数所在的对象的起始地址,在函数中可以用this指针代表当前对象来使用(除了静态成员函数,静态成员没有this指针)

对this指针解引用就是当前对象:(*this).name

this指针的类型:this指针指向的对象是什么类,this指针就是什么类型

常成员对象

常对象:

将类对象声明为常量

特点:1,长对象必须要有初始值  2,常对象中的数据成员都不能被改变

person const p1("lili",20);

常量数据成员:

只能通过构造函数的参数初始化表对常数据成员初始化,任何其他函数都不能对常数据成员赋值

如果有常数据成员,就没有默认构造函数,就必须有参数初始化列表,因为,必须利用初始化列表对常数据成员初始化。

常成员函数:

如果将成员函数声明为常成员函数(const放在小括号之后),则只能引用本类中的数据成员,而不能修改它们

c++面向对象程序设计中,经常用常指针和常引用作函数参数。这样数据不能被随意修改

类对象之间的赋值:

类和结构体都可以在同类之间进行赋值

对象的复制(克隆):

类名  对象1(对象2)---调用拷贝构造函数

类的赋值和复制都会达到相同的目的,只是,赋值给旧对象的叫做赋值,赋值给新对象的同时创建新对象叫克隆

 类的静态数据成员

首先要明白类的对象在建立时发生了什么:
类的对象在建立时进行了类的数据成员的拷贝,每一个对象都有自己的数据成员,但是并不是拷贝类的所有数据,成员函数,静态成员函数和静态成员变量就是共享的,不拷贝的。
被static修饰的类的数据成员(函数和变量),表明该数据成员对该类所有对象都只有一个实例。即该数据成员归所有对象共同享用。
1:类的静态成员函数是属于整个类而非类的对象,所以它没有this指针,这就导致 了它仅能访问类的静态数据和静态成员函数。
2:不能将静态成员函数定义为虚函数。(静态非虚)
3:静态成员初始化与一般数据成员初始化不同:
初始化在类外进行,而前面不加static。
如果要在类内初始化,要为静态成员变量提供const或者constexpr修饰,并且初始值必须是常量表达式。(静态成员函数不能声明为const,也就是不能在函数之后加const)
初始化时不加该成员的访问权限控制符private,public等;
4:默认初始化为0:在静态数据区,内存中所有的字节默认值都是0x00,某些时候这一特点可以减少程序员的工作量( 全局变量也具备这一属性,因为全局变量也存储在静态数据区 )
5:可以通过类的对象,引用和指针来访问类的静态成员,也可以直接用类名和作用域运算符来访问静态成员

person::allPerson();
//p2.display();

继承和派生:

继承的是父类的特性,派生的是具有父类特性的子类

单重继承:一个派生类继承一个基类

多重继承:一个派生类继承于多个基类

派生类把基类全部的成员(不包括构造函数和析构函数)接收过来,也就是说是没有选择的,不能选择接收其中一部分成员

三种继承方式:

共有继承:

私有继承:

外部和派生类虽然不能直接访问基类的私有成员,但是可以通过基类的共有成员函数访问间接地访问私有成员。

保护继承:

保护权限:(protected)

不可以被外界访问,但是可以被派生类访问

直接派生:直接继承  B从A继承

间接派生:间接继承   B从A继承,C从B继承

派生类数据成员和基类数据成员重复情况:

可以在派生类中声明一个与基类成员同名的成员,则派生类中的新成员会覆盖基类的同名成员 但应注意:如果是成员函数,不要求函数名相同,而且函数的参数表(参数的个数和类型)也应相同,如果不相同,就成重载函数了

如何访问基类被覆盖的数据成员:

使用同名数据成员时,用作用域运算符指明所属对象即可

派生类:A       基类:B

A,B中都有display()

访问B中的display()

同过A的对象:a

a.B::dsiplay();//A 的对象a的基类B的display()函数

继承的数据成员如何使用:

继承的就是自己有的,可以和一般成员一样使用

继承后端访问属性:

基类的成员函数只能访问基类的数据成员

基类的构造函数的初始化:

派生类的参数化列表直接调用基类构造函数

住:定义的时候才有初始化,才写参数初始化列表,声明的时候没有初始化,不用写参数初始化列表,也不要调用基类构造函数

基类和派生类构造函数和析构函数执行的先后顺序:

先执行基类的构造函数,再执行派生类的构造函数

先执行派生类的析构函数,再执行基类的析构函数

类中的数据成员是一个类:

数据成员称为子对象

子对象的初始化,即构造函数调用如同基类,在初始化参数列表初始化

执行派生类构造函数的顺序是:
(1)调用基类构造函数,对基类数据成员初始化;
(2)调用对象构造函数,对子对象数据成员初始化;
(3)再执行派生类构造函数本身,对派生类数据成员初始化。

基类--->子对象--->本身

析构函数相反

多层派生的构造函数:

子需要调用直接基类进行初始化即可

构造函数执行顺序:

先从此进入到最基层,从最基层开始执行构造函数

析构函数相反

如果子对象和基类无构造函数,或者构造函数无参,可以不用初始化

基类和子对象构造函数重载:

如果在基类中既定义无参的构造函数,又定义了有参的构造函数(构造函数重载),则在定义派生类构造函数时,既可以包含基类构造函数及其参数,也可以不包含基类构造函数(在调用派生类构造函数时,根据构造函数的内容决定调用基类的有参的构造函数还是无参的构造函数》编程者可以根据派生类的需要决定采用哪一种方式。(可有可无)

同时继承多个基类:

class student:public person,private student1,protected animala{};
派生类构造函数名(总参数表) :基类1构造函数(参数表),基类2构造函数(参数表),基类3构造函数(参数表列)

{派生类中新增数成员据成员初始化语句}

构造函数执行顺序按照声明派生类时继承的顺序执行:

person--->student1--->animal

虚基类 为什么需要虚基类:

当中间基类继承于同一个基类,而这些中间基类派生的派生类就会产生多份相同的基类数据成员

虚基类的功能是声明一个基类为虚基类,则它的多个派生类的公共派生类就只会保留这个虚基类的一份数据成员

每一个中间基类都需要把原始基类声明为虚基类,没有声明虚基类的那份数据依然会被保存,因为虚基类在哪个类声明,就只对哪个类有用

class A;

class B:virtual public A,

class C:virtual public A

派生类对直接虚基类和原始基类的初始化:

如果如同之前多重继承,派生类只需要对虚直接基类初始化,直接虚基类再去初始化自己的基类,那么多个虚基类都对原始基类初始化,则会产生矛盾。所以:

对于虚基类的继承方式,所有基类都由派生类用初始化列表初始化。

当然,对于单一线性继承的没必要

C++系统只执行最后一次对原始基类的初始化,中间基类对原始基类的初始化不会被执行。

但是中间基类构造函数有多少参数,还是要传多少参数

派生类给基类赋值:

赋值只是对数据成员,对成员函数不存在赋值

赋值的过程都是将派生类从基类继承来的数据成员的值赋给基类对应的数据成员

基类不可以向派生类赋值

1,派生类对象向基类对象赋值: 2,派生类对象给基类引用对象赋值:

引用不是整个派生类对象内存,只是派生类对象中继承于基类部分的 内存的引用

所以引用不可以访问派生类对象自己的数据成员

3,派生类对象给基类指针对象赋值:

和引用同理

4,当函数参数是基类对象或者基类对象的引用或者指针时,实参也可以用派生类

性值属于基类的对象都不能越界访问派生类新增的数据成员和成员函数

如何在一个中调用另一个类的数据成员和函数:

使用::

(如:在派生类中需要调用基类数据成员和成员函数时)

类的组合

就是一个类中创建另一个类的对象

友元: 友元函数:

为什么要在类内声明友元函数:

将一个函数声明为类的友元函数,就可以利用这个函数的功能 *** 作类中的数据成员

类的公共成员类的外部函数不用声明为友元函数也可以引用,声明为友元主要是为了能引用类的私有成员

哪些函数可以成为友元函数:

任何不属于类的成员函数,以及类的成员函数都可以成为友元函数

当友元函数是其他类的成员函数时,需要用作用域运算符指明函数所属的类

友元函数没有this指针,即使在类内声明,也不可以直接访问类的成员,要用类对象或者作用域运算符指明

友元函数的访问权限:

可以访问类中的所有成员

友元函数的调用:

友元函数的调用和类无关,对类的访问才和类有关

友元类:

将一个类B声明为另一个类A的友元类,则B中的所有函数都是A的友元函数,都可以访问A的成员数据

类对象数组:

student stu[n];

有n个对象,就会执行n次构造函数

如何给对象数组赋值:

n=1时;

student stu[1]={"name",12};

n=3时:

student stu[3]={student("name1",12),student("name2",13),student("name3",14)};

参数默认值:

string:

string 是标准库中的一个类,string对象可变长序列

string对象的初始化:

string s(5,'h');

则s所代表的内存存放的是:hhhhh,5个h

string的数值转换:

1,其他类型转string:

int n=10;

string s=to_string(n);

 其中要转换为数值的string对象中的第一个字符必须是数字

p,b参数可以不要,只要s.

命名空间

命名空间的本质就是开发者根据需要开辟出一块有名字的内存空间

命名空间------>给空间命名

这快空间的内存就是这个被命名空间的名字

namespace(命名空间),用户可以用namespace关键字定义不同的命名空间,来防止某些函数,类,模板等的冲突(重复)。
命名空间解决的问题:同名冲突

命名空间的命名空间成员可以是声明的形式,也可以是定义的形式

命名空间中的成员是全局成员,只是包含在命名空间中了,使用这些成员时,要使用命名空间限定的形式:ns::a

命名空间成员类型:
变量(可以初始化)
常量
函数

结构体
模板
命名空间(命名空间的嵌套)

命名空间的别名:
用来代替较长的命名空间
生成:namespace TV=Televsion;

使用using关键字生命命名空间的成员
在using语句作用范围内使用using声明命名空间的某个成员之后,在这个作用域内使用这个成员都可以不用命名空间限定。
using 关键字一次只能声明一个命名空间成员

using namespace 命名空间;
可以一次声明命名空间的所有成员,但是使用的前提是不能有同名冲突,如果有同名就必须用::指定

命名空间中的成员在本文件可以当作全局变量来使用

无名命名空间只能在本文件使用

命名空间注意事项:
1:命名空间的定义只能写在全局上,不能写在局部范围内
2:命名空间可以嵌套
嵌套命名空间中成员的调用:

3:命名空间可以随时入新成员:
#include “iostream”
namespace A
{
int a;
int b=10;
}
namespace A
{
int c;
}
int main()
{
}

4:命名空间定义后无分号

5:包含了头文件,也要指明头文件中的命名空间,不要一位包含了包含命名空间的头文件就可以

大型程序往往会使用多个独立开发的库,这些库又会定义大量的全局名字,如类、函数和模板等。当应用程序用到多个供应商提供的库时,不可避免地会发生某些名字相互冲突(重复)的情况。多个库将名字放置在全局命名空间中将引发命名空间污染(namespace pollution)。

多态:

一个事物多种形态

采用静态绑定实现的多态就称为静态多态性,是通过函数重载和运算符重载,在编译时通过静态绑定实现的。采用动态联编实现的多态就称为动态多态性,是通过继承和虚函数,在程序执行时通过动态绑定实现的。平常所说的面向对象程序设计的多态性常指运行时的多态性(动态多态性---继承和虚函数)。

静态多态性:就是函数重载,运算符重载

多态多态性:就是继承和虚函数

静态绑定和动态绑定最直接的区别:

静态绑定:通过对项名调用虚函数

动态绑定:通过基类指针调用虚函数

虚函数: 如何让实现同一接口:

定义一个指向基类的指针或者引用(可以将派生类对象赋值给这个指针或者引用)

实现:

将基类中的于派生类"相同"的函数声明为虚函数----virtual

这样,"相同"的函数在派生类中就不存在了,而被隐藏了,通过接口(指向基类的指针或者引用)不同类中的"相同"函数

在声明时才需要加virtual,定义时,不需要加virtual。

注意虚函数不是函数重载

当中国"相同"函数在基类中被声明为虚函数后,派生类中的“相同”函数都会自动被声明为虚函数,但是最好加上virtual.

虚函数是会被继承的,只是当派生类中有相同函数时,虚函数不被使用。

virtual只能运用在类中声明虚函数,不能用于普通函数

如果把派生类中的”相同“函数声明为虚函数呢:

派生类中的虚函数对基类不会有影响,只对自己的派生类有影响

虚函数如何实现多态性:

对于具有继承关系的同一类族中不同类的对象,对同一函数调用会有不同响应。

(调用同一类族的不同类对象的同名函数,会有不同响应

虚析构函数:

特别的,当动态生成一个指向派生类的基类指针时,如果指针指向的基类之前的所有基类析构函数都是非虚的,那么如果程序执行完毕用delete释放这个指针时,被执行的析构函数就只有指针类型的基类的析构函数,和此基类之前的析构函数,派生类的析构函数将不会被执行。

解决办法是,将基类的析构函数声明为虚析构函数,一旦基类的析构函数变为虚析构函数,那么所有派生类的析构函数都自动变为虚析构函数。这样,当基类类的指针变量指向派生类而被释放时,就可以都执行所有类的析构函数了。

构造函数不能声明为虚函数

纯虚函数:

形式:

virtual 函数返回类型  函数名(参数)=0;

或者:virtual  函数返回类型  函数名(参数)const=0;

为了实现多态性,在基类中只声明一个纯虚函数,不定义,留给派生类定义

”=0“是将函数初始化为0,为了告诉编译器这是一个纯虚函数

纯虚函数只有被定义之后才可以被调用

纯虚函数如果在 派生类中没有被定义,那么在派生类依然是纯虚函数

注意:纯虚函数声明时没有函数体

抽象(基)类:

包含纯虚函数的类都是抽象类

抽象类不能建立对象,只能用来派生派生类,都是抽象类可以用来定义指针。

如果派生类把所有纯虚函数都定义了,那么这个派生类就不是抽象类,就可以用来定义对象,否则也是抽象类。

因为抽象类是作为公共接口使用,所以,最好定义其成员函数和数据成员权限为public

重载函数:

三要素:函数同名,参数个数相同,参数类型相同

运算符重载: 为什么程序员定义一个运算符的新功能是运算符的重载:

既然是运算符,那就说明运算符已经有实现功能的函数了,程序员再定义就是重载

运算符重载是通过定义运算符的函数实现的,所以运算符重载也是函数重载

运算符被重载后,其原有功能依然还存在

为什么需要运算符重载(什么时候需要运算符重载函数):

需要对非标准类型对象进行运算

当运算符 *** 作的数据类型不是标准类型时

如:类

运算符重载实现形式:

函数返回值类型  operator  运算符名称(形参列表)

{函数体}

函数名由operato和运算符组成

  1. complex complex::operator+(const complex &A) const{
  2. complex B;
  3. B.m_real = this->m_real + A.m_real;
  4. B.m_imag = this->m_imag + A.m_imag;
  5. return B;
  6. }

当执行c3 = c1 + c2;语句时,编译器检测到+号左边(+号具有左结合性,所以先检测左边)

C++编译系统会c1+c2解释为:c1.operator+(c2)

c1.是引用自己的成员

opertaor+(c2)就是c1中运算符重载函数,c2是函数的实参

运算符重载规则:

1,C++不允许用户定义新的运算符,只能对已有运算符重载

2,重载运算符函数不能有默认参数 

3,重载运算符的运算对象必须至少有一个是用户自定义的类型对象,不能全是标准类型

4,用于类对象的运算的运算符一般必须重载,两个除外:=,&

=:同一类型类对象可以相互赋值

&:地址运算符

5,当运算符是类的成员函数时,因为+是左结合性,左边的运算对象必须是非标准类型,否则无法调用重载函数。如果左边是标准类型可以让重载函数成为友元函数,而不是成员函数

6,重载不改变运算符的结合性,优先级,运算对象的个数

7,友元函数中实参和形参必须一 一对应:

friend complex operator+(int& i,complex& c){}

c3=1+c2;//right

c3=c2+1;//error

8,只能作为成员函数的运算符:=,[ ]-->下标运算符,(),->成员运算符,单目运算符,复合运算符。

单目运算符:!a,&a,++a,a--等等

复合运算符:<=,>=,==,!=等等

只能作为友元函数:<<,>>,双目运算符

运算符作为类成员函数和友元函数的区别:

1,作为成员函数,其中一个运算对象就是引用对象,参数会比友元函数少一个

2,作为友元函数,运算对象都需要作为参数传入

重载运算符作为友元如何引用:

和作为类成员函数一样引用

编译器检测到运算对象不是标准类型会自动去寻找重载函数

注意:运算符重载并不是说一定要用这个运算符进行运算,而只是用这个运算符重载一个函数来完成一定的功能

运算符重载函数的调用:

和普通函数一样:函数名(实参列表)

 if(operator>(s1,s2))
                cout< "<  

单目运算符重载: 自增,自减运算符重载:

class A;

怎么区分是前置自增(++a)还是后置自增(a++)

前置:重载函数形参列表要么为空,要么就和实参数目一样

后置:要求后置重载函数形参不能为空,无实参则至少也要有一个int型参数,有实参则添加实参个数:

自减一样

1:

Time operator++()//前置

Time operator++(int)//后置

2:

void operator++(num& n1)//前置
{
        ++n1.n;
}
void operator++(num& n1,int)//后置
{
        n1.n++;
        n1.n++;
}
 

<<和>>运算符重载:

只能将<<和>>运算符重载函数作为友元函数,不能作为成员函数

形式:

class A;

A a;

>>:

istream&  operator>>(istream& input,A& b){}

<<:

ostream&  operator<<(ostream& ouput,A& b){}

input,output,b都是形参

这两个运算符的重载函数的返回类型和第一个参数,必须是istream&或者ostream&,第二个参数是类对象的引用

为何:

看:cout<

首先执行cout<

返回是为了继续执行

eg:cout<

cout是ostream类的对象,是第一个参数

cin,cout:

C++编译系统在类库中都会有输入流类istream和输出流类ostream,

cin是istream类的对象,cout是ostream类的对象

cin:
可以连续输入不同类型数据:

int a;

char c;

float b;

cin>>a>>b>>c;

可以往指针所指地址输入数据:

printf("Please input name for the %d person:n",i);
                cin>>per[i]->name;
 

ostream的三个对象:cout,cerr,clog

cerr和 clog用来输出语句的错误信息,cerr不经过缓冲区直接输出,clog需要将数据存放在缓冲区,在缓冲区满或者遇到endl之后才输出

eg:cerr<<"I want to fall in love."<

clog<<"I want to fall in love."<

cout对象有一个成员函数用来输出一个字符:cout.put('a');

同样cin也有一个成员函数用来读入一个字符:cin.get();

1,int a=cin.get();

2,也可以:cin.get(a);

成功返回非0,失败返回0

getchar(a)

3,cin.get(字符数组名/字符指针,n,终止字符)

读取n-1个字符到数组中,如果读取过程遇到种植字符,就终止读取

eg:char arr[10];

cin.get(arr,10,'a');

同样:cin.getline(字符数组名/字符指针,n,终止字符)

注意:终止字符不被读入

c和c++强制类型转换:

c:

int a=10;

(float)a;

c++:

float(a);

当然c++也可以用c的方式,但是c++中最好用c++类型

流:

流动的字节序列

内存----》输出设备

输入设备----》内存

缓冲区:暂存数据

比如从键盘输入,并不是一个一个输入到内存,而是先输入到键盘缓冲区,当按下回车,缓冲区的数据才被发送往内存

cout,cin都有缓冲区,当cout遇到endl,就将缓冲区数据输出到终端

异常处理:

对运行时可能出现的错误和其他情况进行处理(比如,输出)

c++异常处理采取的办法:

在父函数将需要检查的程序(子函数的调用)放入try模块,由子函数(使用throw)抛出异常信息,抛出的信息抛到try模块中,当子函数设置的异常条件满足,throw抛出异常,子函数终止,try模块由于子函数异常终止,也终止,因为try异常终止,所以程序会跳到catch语句去捕捉throw抛出的信号,如果捕捉到,就进入catch语句执行对异常的处理。(如果父函数不能处理,可以将异常抛给父函数的父函数,由上一层函数处理,可以逐级上传)。(子函数抛出,父函数处理)。(throw,try-catch也可以在一个函数中)

C++异常处理由3部分组成,检查(try),抛出(throw), 捕捉(catch)

try:需要检查的语句放在try中

throw:在子函数中抛出异常到父函数try模块中

catch:捕捉异常并处理

(catch异常处理器/块)

try和catch是一个整体,当try中的语句没有发生异常是,就不回去执行catch中的语句,

只有当try中的语句发生异常,程序才会跳到catch中的语句去捕捉异常并处理。

可以在函数声明和定义时,限定可能抛出的异常类型:

int   func()  throw (int,double,float);//声明时

int   func()  throw(int,double,float){

...

}

则表明,抛出的异常只能是,int/double/float。

如果使用这种限定的形式,则函数声明和定义时都必须有异常限定部分:throw(int,double,float)

如果异常限定中throw()为空,则表示不抛出任何异常信息。如果抛出了,程序将终止。

注意:

1,try和catch是一个整体,catch不能单独使用,try可以单独使用。

2,try和catch中间不能插入其他语句。

eg:

try{

......

}

cout<<"dfcdcdc"<

catch{

........

}//error

3,当catch处理完,程序并不会终止,而是继续执行catch之后的语句。

4,try{}---catch{}的花括号不能省略,即使只有一个语句也不能。

5,一个try-catch语句中只能有一个try,但是可以有多个catch,用来捕捉不同类型的信号。

try{...}

catch{...}

catch{...}

catch{...}

...

6,catch只能检查捕捉到的信号的类型,不会检查捕捉到的信号的值。所以catch()括号中一般只写异常信号类型。所以要抛出不同异常信息,需要抛出不同类型数据。

eg:

int a,b,c;

...

throw a;/throw b;/throw c;都是相同的。

7,throw抛出的数据不仅可以是C++预(先)定义的标准类型,还可以是用户自己定义的类型,如类,结构体等。

8,catch语句可以获取捕捉到的数据的一份拷贝:

定义catch为:catch(double a)

那么,当抛出一个double变量b时,就会拷贝b的值给a,就可以在catch模块中使用a变量。

9,catch(...)可以捕捉所有类型的信号,catch(...)应该放在最后,如果放在前面那,那么catch(...)之后的语句就没有用了,根本不会被执行。

9,throw和try-catch可以在同一个函数中,也可以在不同函数中,当throw抛出时,先在本函数找catch模块,如果异常被处理,就不进入父函数,如果本函数的catch没有捕捉throw抛出的信号,程序就转到上层函数寻找符合要求的catch,逐层向上,知道信号被catch捕捉或者整个程序都没有捕捉这个信号的catch,则程序会都调用teminate函数使函数终止。

10,当throw和try-catch在一个函数中时,

11,catch{

throw;

}

程序会把catch捕捉到的信号再抛给上层函数检查和处理。

12,因为抛出是在try中抛出,所以throw可以写在try中;

try{

throw a;

}

catch(){}

13,try-catch,throw可以在父函数,子函数中都有。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/4752061.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-08
下一篇 2022-11-08

发表评论

登录后才能评论

评论列表(0条)

保存