1) C++中,结构体内部可以有函数,C语言中不能有函数。
2) C++中,结构体和类可以通用,结构体是一种特殊的类,特殊性体现在它是struct定义的类。
3) 结构体,缺省的成员访问类型是public,类的是private。
4) 类的一个实例就是一个对象。
5) 面向过程和面向对象的比较:比如,打开一个收音机。面向过程的实现方式是实现一个打开函数,收音机是以参数的方式传进来,然后打开对应的收音机。面向对象的实现方式是实现一个收音机类,收音机本身具有了打开功能,我们只需去调用打开功能就可以了。
6) 构造函数最重要的作用是创建对象本身。
7) 当定义一个对象时,构造函数也执行了,里面可以对对象内部数据成员进行初始化。
8) 定义一个类时,会占用存储空间,这个空间就是构造函数的所占据的。
9) C++规定,每个类必须有一个构造函数,没有构造函数,就不能创建任何对象。
10) 如果用户没有创建构造函数,那么编译器会提供一个默认的构造函数。它只负责创建对象,而不做任何的初始化工作。
11) 析构函数,用来对对象的内存释放。
12) 构造函数和析构函数是由系统调用的。
13) 构造函数可以带参数,析构函数不能有参数。比如,在构造函数中申请的堆内存,可以在构造函数中进行释放。
14) 构造函数可以有多个,这样的方式较重载。
15) 函数的重载。构成的条件:函数的参数类型、参数个数不同,才能构成函数的重载。
16) 不能构成重载的函数:
i. 第一种情况 1)void output(); 2)int output();
ii. 第二种情况 1)void output(int a ,int b=5); 2)void output(int a);
17) 下面这段程序结果为多少呢?
#include<iostream.h>
class Point
{
public:
int x;
int y;
Point()
{
x=5;
y=6;
}
Point(int a,int b)
{
x=a;
y=b;
}
~Point()
{
}
void output()
{
cout<<x<<endl<<y<<endl;
}
void output(int x,int y)
{
x=x;
y=y;
}
};
void main()
{
Point pt(3,3);
pt.output(5,5);
pt.output();
}
上面的程序输出为3,3。主要是因为变量的可见性问题。
如果我想输出5,5。应该这样去写代码:
void output(int x,int y)
{
this->x=x;
this->y=y;
}
18) This指针式一个隐含指针,它是指向对象本身,代码对象的地址。
19) 继承的方式:public private protected
20) Protected:在基类中定义的protected方式。子类中可以使用,但是外面不能使用(比如main函数中)。
21) Public:任何地方都可以被访问。
22) Private:子类中都不能被访问。
23) 类的继承访问特性
基类的访问特性 | 类的继承特性 | 子类的访问特性 |
Public Protected Private | Public | Public Protected No access |
Public Protected Private | Protected | Protected Protected No access |
Public Protected Private | Private | Private Private No access |
24) 子类继承父类。子类和父类都有构造函数和析构函数。当定义一个子类时,先调用父类的构造函数,再调用子类的构造函数;继而,先调用子类的析构函数,再调用父类的析构函数。总之,其调用顺序是相反的。
如下面代码所示:
#include <iostream.h>
class Animal
{
public:
Animal()
{
cout<<"Animalconstruct"<<endl;
}
~Animal()
{
cout<<"animaldeconstruct"<<endl;
}
voideat()
{
cout<<"animaleat"<<endl;
}
voidSleep()
{
cout<<"animalsleep"<<endl;
}
voidbreathe()
{
cout<<"animalbreathe"<<endl;
}
};
class Fish : public Animal
{
public:
Fish()
{
cout<<"fishconstruct"<<endl;
}
~Fish()
{
cout<<"fishreconstruct"<<endl;
}
};
void main()
{
Fishfh;
}
输出结果:
25) 子类中常量的初始化
class Fish : public Animal
{
public:
Fish():Animal(400,300),a(1)//下面常量的初始化
{
cout<<"fishconstruct"<<endl;
}
~Fish()
{
cout<<"fishreconstruct"<<endl;
}
private:
constint a;
};
26) 函数的覆盖:发生在父类与子类之间的。
27) 函数的重载是发生在一个类之间的。
#include <iostream.h>
class Animal
{
public:
Animal(intheight,int weight)
{
cout<<"Animalconstruct"<<endl;
}
~Animal()
{
cout<<"animaldeconstruct"<<endl;
}
voideat()
{
cout<<"animaleat"<<endl;
}
voidSleep()
{
cout<<"animalsleep"<<endl;
}
voidbreathe()//不是虚函数
{
cout<<"animalbreathe"<<endl;
}
};
class Fish : public Animal
{
public:
Fish():Animal(400,300),a(1)
{
cout<<"fishconstruct"<<endl;
}
~Fish()
{
cout<<"fishdeconstruct"<<endl;
}
void breathe()//函数覆盖
{
Animal::breathe();//也有父类的方法
cout<<"fishbubble"<<endl;
}
private:
constint a;
};
void fn(Animal *pAn)
{
pAn->breathe();
}
void main()
{
Fishfh;
Animal*pAn;
pAn=&fh;//类型转换。但是animal和fish的this指针都指向相同的地址。fish和animal的存储空间模型是一致的。但是fish会被截取掉。
fn(pAn);
//fh.breathe();
}
输出结果:
当把父类的breathe方法前面加上virtual时,就会输出”fish bubble”。这就是所为的多态。如下图所示:
28) 多态性:当C++编译器在编译的时候,发现Animal类的breathe函数是虚函数,这个时候C++就会采用迟绑定的技术,在运行时,依据对象的类型(在我的程序中,我们传递的Fish类对象的地址)来确定调用的哪一个函数,这种能力就叫做C++的多态性。
29) 如果不加virtual,这个时候的breathe函数在编译的时候就已经绑定,如果加上virtual变成虚函数,breathe函数在运行的时候绑定,也就是迟绑定。
30) Virtual函数总结一句话:子类有的,先调用子类的,子类没有的就调用父类的。
31) 纯虚函数是没有函数体的,如:virtual void breathe()=0;。这样的类是抽象类,它是不能实例化对象的,也就不能派生其他类。除非,你把这个纯虚函数实例化。比如,子类的fish中,你实例化了breathe函数:
void breathe()//函数覆盖
{
cout<<"fishbubble"<<endl;
}
如下面的代码:
#include <iostream.h>
class Animal
{
public:
Animal(intheight,int weight)
{
cout<<"Animalconstruct"<<endl;
}
~Animal()
{
cout<<"animaldeconstruct"<<endl;
}
voideat()
{
cout<<"animaleat"<<endl;
}
voidSleep()
{
cout<<"animalsleep"<<endl;
}
virtualvoid breathe()=0;
};
class Fish : public Animal
{
public:
Fish():Animal(400,300),a(1)
{
cout<<"fishconstruct"<<endl;
}
~Fish()
{
cout<<"fishdeconstruct"<<endl;
}
voidbreathe()//函数覆盖
{
//Animal::breathe();//也有父类的方法
cout<<"fishbubble"<<endl;
}
private:
constint a;
};
void fn(Animal *pAn)
{
pAn->breathe();
}
void main()
{
Fishfh;
Animal*pAn;
pAn=&fh;//类型转换。但是animal和fish的this指针都指向相同的地址。fish和animal的存储空间模型是一致的。但是fish会被截取掉。
fn(pAn);
//fh.breathe();
}
32) 引用:实际上就是变量的别名。
33) 引用和指针变量的内存模型:
指针需要内存空间,引用不需要占据内存。
34) 引用一般用于函数传参,还有就是可读性更强一些。
35) 比如定义一个函数,去交换两个变量的值。
int change(int &a,int &b)
{
}
void main()
{
intx=3;
inty=4;
change(x,y);//这里告诉你,x和y就是用来交换的。
}
上面这段代码要比指针的书写要清楚些。
int change(int *pA,int*pB)
{
}
void main()
{
int x=3;
int y=4;
change(&x,&y);/这样的话,会让人少许有些误解。
}
36) 有些时候,可以这样去编写代码:
class Animal
{
public:
Animal(intheight,int weight)
{
cout<<"Animalconstruct"<<endl;
}
~Animal()
{
cout<<"animaldeconstruct"<<endl;
}
voideat();
voidSleep()
{
cout<<"animalsleep"<<endl;
}
virtualvoid breathe()=0;
};
void Animal::eat()
{
cout<<"animaleat"<<endl;
}
37) 防止类的重复定义问题,如下面代码所示:
class Point
{
};
class Point
{
};
void main()
{
Pointpt;
}
这时候就会有错误:
这个时候,我们加上预编译指令就可以解决这个问题。
#ifndef POINT_H_H //定义宏,一般用大写
#define POINT_H_H
…….
#endif
避免重复包含。
#ifndef POINT_H_H //定义宏,一般用大写
#define POINT_H_H
class Point
{
};
#endif
#ifndef POINT_H_H
#define POINT_H_H
class Point
{
};
#endif
void main()
{
Pointpt;
}
38) 编译过程,工程目录及代码如下:
编译过程如下:
欢迎读者批评,指正。 By 刘洼村