一, 输入输出函数
标准输出函数cout,其语句的一般格式为
Cout<<表达式;
Cout<<表达式<<endl;(endl为换行符相当于‘\n’)
Cout<<表达式1<<表达式2<<……<<表达式n;
同理标准输入函数cin其语法格式为
Cin>>表达式1>>表达式2>>表达式n>>endl;
C++的标准输入输出于C语言来说比较方便,不需要输入输出格式说明
只要用到标准输入输出函数就必须调用库函数输入输出流库<iostream>和域名空间using namespace std.
二、字符串变量
C++中多了一种字符串类型的变量(string),定义格式为string s;可以直接进行字符串变量的加比较。例如,string s1=“你好”;string s2=“世界”;s1+s2=“你好世界”,表达式s1>s2如果正确返回1,否则返回0.使用string变量同样需要调用流库<iostream>和域名空间using namespace std.
三、引用
Int &b=a;这里就是一个引用,相当于给变量a引用一个新的名字b,b不会再内存开辟空间,这个时候a和b完全等价,指同一块内存变量,操作a就相当于操作b。引用主要用于传递函数的参数和返回值,例如:
Void change1(int &r)
{
r=100;
}
Void change2(int *p)
{
*p=1000;
}
void main()
{
Int a=1;
change1(a);
cout<<a<<endl;
change2(&a);
cout<<a<<endl;
}
结果输出100,1000.可见引用传递和指针传递都可以改变参数的值。
引用的使用需要注意以下几点:
1. 引用必须在声明时立即初始化,不允许空引用;
2. 引用一旦初始化,就不能再更改,不能再引用其他数据;
3. 引用和被引用的变量实际上代表同一个内存的数据。
四、类
类是具有相同属性和行为的一组对象的集合,主要包括属性和行为两个部分。类类型是C++中最主要的数据结构类型。
声明一个类类型
class student{
public;//访问权限修饰符
int num;//声明成员变量
char sex;
char name[20];//声明成员函数
void display();
}
Void student::display()
{
}//成员函数声明后必须要定义,可以在成员函数声明时就定义,也可以在类类型声明外面定义,在外面定义格式为 函数返回值+类类型+双冒号+函数名+函数参数
定义一个对象: 类型名 对象名(构造函数参数)//构造函数的参数默认为空
对象只保存成员变量,存储在栈中,函数在代码区。访问对象的成员变量的方法:
对象.成员
对象.成员函数(实参列表)
对象的指针->成员变量
对象的指针->成员函数名(实参列表)
对象的引用.成员变量
对象的引用.成员函数(实参列表)
类名::静态成员变量名
类名::静态成员函数名(实参列表)
五.成员函数重载
成员函数的名字一样但是成员函数参数不同包括参数的类型、个数、顺序不一样的现象叫做成员函数重载。成员函数的重载与函数的返回值无关。判断两个成员函数是否重载的最总取决于将同样的参数放在函数名一样的成员函数中看是否出现歧义,若没有歧义则是同一函数,若有歧义则是成员函数重载。
六、构造函数
构造函数是一种特殊的成员函数:
1, 构造函数的名字必须要与类名相同
2, 它不具有任何类型,不返回任何值
3, 构造函数的功能是由用户定义的
4, 构造函数可以重载
5, 构造函数在生成对象时就执行
构造函数的声明格式为:
类名();
类名(形参列表)
关于构造函数一些总结
1. 如果一个类中没有自定义构造函数,那系统会默认生成一个public修饰的无参数空实现的构造函数
2. 当自定义了构造函数,则默认的无参数的构造函数就不存在了
3. 创建对象的时候会调用类中其中的一个构造函数
4. 构造函数可以重载,创建对象时具体调用哪一个构造函数由传参数来确定
5. 构造函数的作用主要用于初始化正在创建的对象
七、成员变量的初始化
采用构造函数初始化成员变量
例如
Stu:Stu(int age,string name){
this->age=age;
this->name=name;//构造函数中的this指正在穿件的对象的地址
}
采用初始化表为对象的成员变量赋值
例如
Stu::Stu(int xage,string xname):age(xage),name(xname){
}
八、析构函数
析构函数名字是类名的前面加一个“~”符号。没有参数,并且不能够重载。
当对象的生命期结束时,会自动执行析构函数。
析构函数一般用于完成“清理”的工作,如果用户没有自定义析构函数,C++编译系统会自动生成一个析构函数,但它什么操作都不进行。想让析构函数完成任何工作时,都必须要自定义析构函数。
构造函数和析构函数按照栈中先进后出的顺序
九、对象数组
数组不仅可以由简单的变量组成,也可以由对象组成(对象数组的每一个元素都是同类的对象)
1. 调用可以不传参数的构造函数创建对象数组
Student stud[50];
2. 调用可以只传一个参数的构造函数有两种方法创建对象数组
Student stu[3]={60,70,80};
或者
Student stu[3]={Student(60),Student(70),Student(80)};
3. 调用可以传多个(这里举例3个)参数的构造函数创建对象数组
Student stu[3]={Student(1001,1,10),Student(1002,2,11),Student(1003,3,12)};
不能够直接Student stu[3]={(1001,1,10),(1002,2,11),(1003,3,12)};这样的话只是把每个括号里最后一个参数传给了构造函数参数列表中的第一个参数,其他的参数并没有传到
十、指针
指向对象成员变量的指针
定义 数据类型名 *指针变量名;
成员变量值的形式:&成员变量指针
指向成员函数的指针
定义指向成员函数的指针变量的形式
1. 无参数成员函数
成员函数返回类型 (类名::指针变量名)();
2. 有参数成员函数
成员函数返回类型 (类名::指针变量名)(参数列表);
成员函数指针变量值的形式:
&类名::成员函数名;
成员函数指针变量使用形式:
(对象.*指针变量名)(实参);
指向对象的常指针和指向常对象的指针
类名 *const 指针变量名;(常指针)
禁止改变指针,指针是不变的
const 类名 *指针变量名;(常对象指针)
禁止改变所指向的对象,对象时不变的
例如:
void f1() {
A a1;A a2;
A * const p = &a1;
//p = &a2;//错,常指针不可改变
p->x = 23;
}
void f2() {
A a1;A a2;
const A *p;
p = &a1;//指针可以改变
//p->x = 23;错,常对象指针指向的对象中的内容不可以改变
p = &a2;
}
void f3() {
A a1;A a2;
const A * const p = &a1;//常对象常指针,指针和对象均不可改变
//p = &a2; 错
//p->x = 34; 错
}
十一、常成员变量和常成员函数以及常对象
常成员变量时用关键字const来修饰声明的成员变量,const位于变量前或者类型前都一样。例如
class Time{
public:
const int hour;
int const minute;
}
1. 常成员变量只能通过构造函数的初始化表对常成员变量进行初始化;
例如.
class A {
public:
//常成员变量
const int x;
const int y;
int z;
A(int x, int y);
A(int x, int y, int z);
};
A::A(int x, int y): x(x),y(y) {
}
A::A(int x, int y, int z): x(x),y(y) {
this->z = z;
}
2. 常成员变量所在类中所有的构造函数都必须通过初始化表对常成员进行初始化;
3. 常成员变量可以向普通的成员变量一样进行访问,但是其值是不能被修改的。
4. 必须在构造函数初始化,不然为随机数没有意义
常成员函数:用const修饰的成员函数,const位于函数括号之后,定义和声明的地方都要加上const。
例如
class Time{
public:
void show()const;
}
常成员函数可以用this访问成员变量,但是不能修改this访问的成员变量。(包括常成员变量和非常成员变量)
常成员函数体重用this访问成员函数时,只能访问常成员函数(常成员函数只能访问常成员函数)
例如
class A {
public:
int x;
//常成员函数
void m1()const;
void m2()const;
void m3();
void m4();
};
void A::m1()const {
cout<<this->x<<endl;
//常成员函数不能修饰成员变量
//this->x = 234;//错
this->m2();
//常成员函数只能访问成常函数,但是不能访问普通的成员函数
//任函数都可以访问常成员函数
//this->m3();//错
}
void A::m2()const {
this->m1();
}
void A::m3() {
this->x = 234;
}
void A::m4() {
this->m1();
}
常对象
定义常对象形式为
类名 const 对象名(实参列表);或者const 类名 对象名(实参列表);
例:
class Time{
public:
Time();
Time(int x);
……
}
const Time time1;
Time const time2;
const Time time3(100);
Time const time4(200);
常对象特点:
1. 常对象中所有的成员变量的值都不能够被修改;
2. 常对象访问函数时只能访问常成员函数;
例:
class A {
public:
int x;
int y;
A();
void m1()const;
void m2();
};
A::A():x(10),y(23) {
}//初始化表初始化成员变量
void A::m1()const {
cout<<"A::m1();"<<endl;
}
void A::m2() {
cout<<"A::m2();"<<endl;
}
void f2() {
const A a;//常对象
cout<<a.x<<endl;//10
cout<<a.y<<endl;//23
//常对象中成员变量的值不能修改,只能在构
//构函数中用初始化表初始化
//a.x = 23;//错
a.m1();
//a.m2();错
//常对象只能访问常成员函数
}
int main() {
f2();
return 0;}
十二、对象动态创建与销毁
new运算符可以动态创建对象,创建对象后会返回对象的地址。如果内存不足导致无法成功创建对象,则会返回0指针值。
动态创建的对象要用“delete 对象指针”才能销毁对象。
BOX *pt1;
pt=new BOX;//创建对象
delete pt1;//销毁对象
如果是用malloca和free动态创建和释放对象的话不会调用构造函数和析构函数,所以一般在C++中是采用new和delete来动态创建和销毁对象的。
十三、对象的复制与赋值
对象赋值方法:
类名 对象1(对象2);
对象赋值的方法:
对象名1=对象名2;
类名 对象名1=对象名2;
例:
//赋值
void f1() {
Stu stu1,stu2;
stu1.age = 20;
stu1.no = 100;
stu2 = stu1;
stu1.show("stu1");
stu2.show("stu2");
Stu stu3 = stu1;
stu3.show("stu3");
}
//复制
void f2() {
Stu stu1;
stu1.age = 20;
stu1.no = 100;
Stu stu2(stu1);//本质是调用了类的复制构造函数
stu2.show("stu2");
}
1.如果自定义了非复制构造函数,类中有一个默认的复制构造函数
2.如果没有自定义构造函数,类中默认有两个构造函数,一个无参的,
另一个是复制构造函数
3.如果是自定义了复制构造函数,类中就一个默认的构造函数都没有了
4.复制构造函数就是:写一个参数为 "类的引用" 的构函数,与函数体无关
十三、静态成员(static)
静态成员变量
如果想在同类的多个对象之间实现数据共享,可以用静态成员变量,即用static修饰的成员变量,例如static int a;
静态成员变量在项目刚开始运行的时候就分配 ,在项目运行结束以后才销毁。
静态成员变量被它所属类创建的所有对象所共享。
静态成员变量必须在类体外初始化。建议放在main函数所在的文件中,格式为:
成员变量数据类型 类名::静态成员变量=初值
例如 int Stu::a=20;
访问静态成员变量有两种方式:
1. 与普通成员变量一样,可以通过对象、对象指针或对象引用来访问
2. 用静态成员变量所属类的类名加双冒号来访问,这种方式只能够访问静态成员变量,即
类名::静态成员变量
class Stu{
public:
static int a;
}
int Stu::a=10;//类名双冒号访问变量
void f1() {
cout<<"Stu::a="<<Stu::a<<endl;//10
Stu stu1;// b, c
Stu stu2;// b, c
cout<<"stu1.a="<<stu1.a<<endl;//10
cout<<"stu2.a="<<stu2.a<<endl;//10 静态成员变量为同类对象所共用
stu1.a = 1000;//普通成员变量一样的方式访问别且修改静态成员变量
cout<<"======================"<<endl;
cout<<"Stu::a="<<Stu::a<<endl;//100
cout<<"stu1.a="<<stu1.a<<endl;//100
cout<<"stu2.a="<<stu2.a<<endl;}//100
静态成员函数
静态成员函数就是static修饰的成员函数
class Box{
static int volume();
}
访问静态成员函数的方式有两种:
1. 与普通成员函数访问的方式一样,可以用对象、指针和引用来访问
2. 静态成员函数所属类类名::静态成员函数,即int class::volume();
3. 静态成员函数中没有this指针,而是用“类名::”来代替this,因此静态成员函数中只能够访问类中的静态成员变量。
例如:class Stu {
public:
static int a;//静态成员变量
int b;
int c;
static void static_m1();
static void static_m2();
void m3();
void m4();};
void Stu::static_m1() {
cout<<"Stu::static_m1()\n";
Stu::a = 23; //可省Stu::
Stu::static_m2(); //可省Stu::}
void Stu::static_m2() {
cout<<"Stu::static_m2()\n";}
void Stu::m3() {
this->static_m1();
Stu::static_m1();}
void Stu::m4() {}
main() {
Stu::static_m1();
Stu::static_m2();
Stu stu1;
stu1.static_m1();
stu1.static_m2();
}
static 的总结
1. static修饰的成员可以像普通的成员一样被访问(对象.成员,对象指针->成员,引用)
2. static修饰的成员函数中不能通过this访问普通的成员,但是可以通过类名::访问静态成员
3. 普通的成员函数中可以通过this访问静态成员,也可以通过类名::访问静态成员
十四、访问权限修饰符
public(公有的)修饰的成员没有限制,都可以访问
protected(受保护的)修饰的成员变量只能够在当前类的(当前类的成员函数)或子类中访问
private(私有的)修饰的成员变量只能够在当前类(当前类中的成员函数)中使用
十五、友元
在一个类中可以有公用的(public)受保护的(protected)和私有的(private)成员,还有一个例外——友元(friend)
类中的私有成员可以在以下四个地方被访问:
1.当前类中
2.类的友元函数;
3.类的友元成员函数;
4.类的友元类的成员函数;
类的友元函数就是在类中把普通函数用friend进行声明
例如:
class Stu{
friend void test2();
Private:
int age;
void show();
}
void test1(){
Stu stu;
stu.age;//错误,私有成员不可以调用
Stu.show();
}
void test2(){
Stu stu;
Stu.age=23;
Stu.show();}//正确,友元函数中可以访问私有成员
类的友元成员函数就是在类中把另一个类的成员函数用friend进行声明,例如:
class Stu{
friend void Teacher::test2();
private:
int age;
void show();
}
class Teacher{
public:
void test1();
Void test();
}
Void Teacher::test1(){
Stu stu;
stu.age=2;//错
stu.show();//错
}
Void Teacher::test2(){
Stu stu;
stu.age=2;
stu.show();//对
}
类的友元类,类的友元类就是在类中把另一个类用friend进行声明,例如:
class Stu{
friend class Teacher
private:
int age;
void show();
}
class Teacher(){
public:
void test1();
void test2();
}
Void test1(){
Stu stu;
stu.age=2;
stu.show();//对
}
void test2(){
Stu stu;
stu.age=2;
stu.show();//对
}
十六、继承与派生
一个派生类只从一个基类派生,这称为单继承。一个派生类从多个基类派生叫做多重派生
派生类的声明方式:class 派生类名:【继承方式】 基类名……【基类n继承方式】基类n类名{
派生类新增加成员
};
假设已经声明了一个基类Student,在此基础上通过继承建立一个派生类Student1:
class Student1:public Student{
public:
int agt;
string addr;
public:
void display();
}
继承方式包括:
public (公用的)
private(私有的)
protected(受保护的)
不写继承方式的情况下默认为private(私有的)
派生
派生类中的成员包括从基类继承过来的成员派生类新增加的成员
派生类不会吸收基类的构造函数和析构函数,派生过程为:
基类成员权限修饰类与继承方式对继承的作用
Public继承
Protected继承
Private继承
基类
private
protected
public
private
protected
public
private
protected
public
派生类
protected
public
protected
protected
private
private
由上面的表格可以知道,基类中的private修饰成员不能够继承,public继承将public继承为public、protected继承为protected,protected继承将基类中protected和public全部继承为protected,private继承将基类中的public和protected全部继承为private。
派生类的构造函数和析构函数
1.派生类的构造函数和析构函数不能够被重写;
2.派生类的构造函数被调用时,会先调用基类中的一个构造函数,因为在派生类的构造函数用了初始化表的方式调用了基类构造函数
3.在派生类对象释放时,先执行派生类析构函数,再执行基类析构函数。
派生类构造函数的形式:
派生类构造函数名(总参数列表):基类构造函数名1(参数列表)……基类构造函数名n(参数列表)
十七、继承的二义性
多基继承的二义性
class A1{
public:
int a
int ;
void m();
}
class A2{
public:
int a;
voidm();
}
class B:public A1,public A2{
}
main(){
B b;
b.2=2;//错
b.m();//错
b.A1::a=2//对
b.A!::m();//对
}
由于一个派生类有多个基类,并且基类中的函数形式一样,这种想叫做由于多基继承产生的二义性,这种情况下在派生类中访问产生二义性的基类中的成员时候需要在成员变量前面指定所在的类名和双冒号(::)。
共同基产生的二义性
如果派生类的基类是一模一样的类,在派生类访问基类的成员变量时候也会出现二义性,那么这种情况叫做共同基产生的二义性。
例如:
class B1:public A{};
class B2:public A{};
class C:public B1,public B2{};
共同基产生的二义性的解决办法——虚基派生
例如:
class B1virtual :public A{};
class B2:virtual public A{};
class C:public B1,public B2{};
这样的话在派生类C中只会出现一个A类,可以直接访问基类的变量不会出现二义性。
派生类对象为基类对象赋值
(1) 派生类对象可以向基类对象赋值。
假如A是B的基类:
A a1;
B b1;
a1=b1;
派生类对象可以为基类对象赋值,这个时候只会把从基类继承过来的成员复制过去,而把派生类自己新增的成员舍弃不用;
(2) 基类声明的指针变量和引用类型变量可以用来指向派生类的对象,但是此时只能通过指针或引用访问从基类中继承下来的成员函数和成员变量
假如A是B的基类:
B b;
A *pa=&b;
A &ra=b;
这个时候指针或引用只能够访问从基类A中继承下来的成员函数和成员变量
十八、组合
1.在A类中以B类的对象作为成员变量的,成为类的组合。
2.在组合时,一般A类的构造函数初始化列表中要初始化B类(会调用B类的一个构造函数),如果没有初始化B类,则调用B类中可以不传参数的构造函数。
例如:
class A{
public:
A();
}
class B{
public:
B(int);
B(int a,int b);
}
class C{
public:
A a;
B b;
C(int x,int y);
}
C::C(int x,inty):b(x){};
//C::C(int x,int y):b(x,y){};
//C::C(int x,int y):a(),b(x){};
//C::C(int x,int y):a(),b(x,y){};
十九、多态性
多态性是面向对象程序设计的一个重要特征。利用多态性可以设计和实现一个易于扩展的系统。多态性是指具有不同功能的函数可以用同一个函数名,这样就可以用一个函数名调用不同内容的函数。向不同的对象发送同一个消息,不同的对象在接到是会产生不同的行为。
多态性分为动态多态性和静态多态性
函数重载和运算符重载实现的多态性属于静态多态性,在程序编译时系统就能够决定用的是哪个重载函数,因此静态多态性又称为编译时的多态性。静态多态性是通过函数的重载实现的。
动态多态性是在程序运行的过程中才动态地确定操作所针对的对象。它又称为运行时的多态性。动态多态性是通过虚函数实现的。
下面来讨论一下虚函数,在讨论虚函数之前必须要弄清楚两个概念
二十、隐藏和重写
隐藏
1.派生类中的成员函数与基类中的成员函数名字相同时,派生类的成员函数会屏蔽基类中同名的成员函数,这种现象叫做隐藏。
2.派生类中的成员变量与基类中的成员变量同名时,派生类的成员变量屏蔽基类中同名的成员变量
3.通过派生类对象、指针、或引用访问基类中被隐蔽的成员时,要在成员前面加上“基类名::”
例如:
//Base.h
class Base{
public:
int x;
void show();
void show(int x);
}
//Child.h
class Child:public Base{
public;
char x;
int show(string name);
}
int main()
{
Child child;
child.x=’c’;//child
child.Base::x=1234;//Base
child.x=1234;//错,这样访问的是child中的x
child.show(“tcc”);//Child
child.Base::show(23);//Base
child.show();//错 参数不对,访问的是child中的show()
}
重写
重写是隐藏的一种特殊情况,当派生类中的成员函数与基类的的名字相同,参数相同,返回值类型相同或者类型兼容,则称为派生类重写了基类函数。
//Base.h
class Base{
public:
void show();
void show(int x);
}
//Child.h
class Child:public Base{
public;
void show();
}
int main(){
Child child;
child.Base::show(12);//Base
child.show();//Child
}
二十一、虚函数
1.在声明函数时,在最前面加上virtual,则该函数就是虚函数,基类的虚函数被派生类继承后仍是虚函数。
2.派生类中可以重写基类的虚函数。
3.用指针访问或引用虚函数时,被访问的虚函数是指针指向的对象所属类的函数(只看指向的对象所属类)。而指针或引用访问重写的普通函数时,被访问的函数是指针或引用类型所属类的函数(只需要看指针或引用类型是什么类)
4.虚函数可以先行动态关联
例如:
class Base {
public:
void show1();
virtual void show2();
};
class Child :public Base{
public:
void show1();
virtual void show2();
int main() {
Child child;
Base *p=&child;
p->show1();//Base
p->show2();//Child
Base &r=child;
r.show1();//Base
r.show2();//Child
return 0;
}
二十二、虚析构函数
(1)虚析构函数即:定义声明析构函数时在前面加virtual修饰,如果将基类的析构函数声明为虚析构函数时,由该基类所派生的所有派生类的析构析构函数也都是自动成为虚析构函数
(2)基类指针p指向用new动态创建的派生类对象child时,用“delete p”删除对象时分两种情况
第一、如果基类中的析构函数为虚析构函数,则会先删除派生类对象,再删除基类对象
第二、如果基类中的析构函数为非虚析构函数时,则会删除基类对象,不会删除派生类对象,这样会出现内存泄露的问题,这个很重要,所以一般把析构函数声明定义成虚析构函数。
二十三、纯虚函数
纯虚函数的定义:
(1)虚函数被“初始化”为0的函数。声明纯虚函数的一般形式是
virtual 函数类型 函数名(参数列表)=0;
(2)纯虚函数没有函数体,由函数体的话就没有意义了
(3)最后面的“=0”并不是表示函数的返回值为0,它只是起了形式上的作用,告诉编译系统“这是纯虚数函数”
(4)这是一个声明语句,最后必须加分号
(5)不能够在当前类中定义
二十四、抽象类
(1)包含有纯虚数函数的类都是抽象类。
(2)不能够用抽象类创建对象(这一点很重要),但是可以用抽象类派生出派生类。也可以用抽象类创建指针或引用来指向抽象类的派生类对象
(3)抽象类的派生类可以把抽象类中的成员变量和成员函数继承下来,包括纯虚函数也会被继承
(4)抽象类的派生类可以实现抽象类的纯虚函数。如果抽象类的派生类没有吧抽象类的纯虚函数实现,那么这个派生类也是抽象类。
(5)纯虚函数被派生类实现以后变成了虚函数
二十五、Protected或private修饰的构造函数
在类的外部创建对象时,不能够调用protected或private修饰的构造函数。
当子类中的构造函数调用父类的private修饰的构造函数时会出错,当子类中的构造函数调用父类中的public或protected修饰的构造函数时是对的
二十六、域名空间
域名空间就是在函数名前面加上自己的名字
例如:
namespace tt{
class Stu {
public:
void study();
};
}
namespace cc{
class Stu {
public:
void study();
};
namespace tt{
void Stu::study() {
cout << "Stu::study() tt \n";
}
}
namespace cc{
void Stu::study() {
cout<<"Stu::Study() cc \n";
}
}
cout和cin函数的域名就是std,所以在使用的时候需要先声明
using namespace std;如果不声明那么在使用cout和cin时需要在函数前面加上”std::”,同理在使用其他自定义的域名空间的时候也需要在使用前声明,或者在使用时加上域名双冒号。使用域名空间可以避免出现同名的函数时会出现歧义的情况,在大型项目时都会用到域名空间