本文的主要内容就是对C++一些知识点的总结,便于自己方便的查看和复习,就是把自己的理解方式记录下来,具体的一些代码什么的有些懒得写,如果还是没看懂的话就去看书好了。
1、结构体和类
在C++中结构体是用关键字 struct 声明的类,类用 class 声明。结构体默认情况下其成员是 public 的,可以在类的外部进行访问,类默认情况下是 private 的,只能在类的内部进行访问。
注意:在定义类时,不要忘了在右括号处加上一个分号“;”。
2、类与对象
类描述了一类事物,以及事物所具有的属性。对象是类的一个实例,对象是可以销毁的,而类是不能被销毁的,类是一个抽象的概念。
注意:在类中定义成员变量的时候,不能直接给成员变量赋值
class point
{
int x=0;//错,不能赋值
int y;
}
3、构造函数
构造函数的名字和类名相同,没有返回值。
作用:构造函数是对对象本身做初始化工作,也就是给用户提供初始化类中成员变量的一种方式。
一个类如果没有定义构造函数,C++编译器会提供一个默认构造函数,不带参数。只要类里面提供了构造函数,不管带不带参数,编译器就不再提供了。
class point//定义一个类
{
public:
int x;//注意不能赋值,赋值的话去构造函数里面
int y;
point()//构造函数,不带参数,名字和类名相同
{
x=0;
y=0;
}
point(int a,int b)//带参数的构造函数
{
x=a;
y=b;
}
}
4、析构函数
格式:~类名()
不允许有返回值,不允许带参数,而且一个类中只能有一个析构函数。
作用:和构造函数相反,用于清除类对象
5、函数的重载
在类中两个构造函数名称一样,只是参数类型和个数不一样,就发生重载。对3中给出的类,执行如下代码:
void main()
{
point pt;//类对象实例
//point pt(5,5);
pt.output();
system("pause");
}
执行pt时输出:0 0,执行pt(5,5)时输出:5 5。函数在执行过程中根据参数类型和参数个数来决定执行哪一个构造函数。
重载构成条件:函数名相同,函数参数类型、参数个数不同。
注意:只有函数的返回类型不同不能构成函数重载,如:
void output();
int output();
同时需要注意带默认参数的情况,如:
//这两种情况是一样的
void output(int a,int b=5);
void output(int a);
6、this指针
this指针是一个隐含的指针,它指向对象本身,代表了对象的地址。一个类的所有的对象调用的成员函数都是同一个代码断的。先贴一段代码
#include <iostream>
using namespace std;
class point//定义一个类
{
public:
int x;//注意不能赋值,赋值的话去构造函数里面
int y;
point()//构造函数,不带参数,名字和类名相同
{
x=0;
y=0;
}
point(int a,int b)//带参数的构造函数
{
x=a;
y=b;
}
void output()
{
cout<<x<<endl<<y<<endl;
}
void input(int x,int y)
{
x=x;
y=y;
}
//void input(int x,int y)
//{
// this->x=x;
// this->y=y;
//}
};
void main()
{
//point pt;//类对象实例
point pt(5,5);
pt.input(10,10);
pt.output();
system("pause");
}
运行结果:5 5
因为变量的可见性,point类的成员变量 x , y在 input 函数中是不可见的,所以我们只是将形参 x 赋给了形参 x ,形参 y 赋给了 形参 y ,根本没有给 point 类的成员变量 x 和 y 进行赋值。
将 input 函数换成
void input(int x,int y)
{
this->x=x;
this->y=y;
}
运行结果:10 10
这个时候成员函数除了接收到2个实参外还接收到 pt 对象的地址,这个地址被隐含的形参 this 指针获取,相当于执行
this=&pt;
7、类的继承
派生类可以继承基类的成员变量和成员方法。
#include <iostream>
using namespace std;
class animal
{
public:
void eat()
{
cout<<"animal eat"<<endl;
}
void sleep()
{
cout<<"animal sleep"<<endl;
}
void breathe()
{
cout<<"animal breathe"<<endl;
}
};
class fish:public animal
{
};
void main()
{
animal an;
fish fh;
an.eat();
fh.eat();
system("pause");
}
虽然 fish 类中没有eat() 函数,但是可以继承父类的.
所以输出结果:animal eat
animal eat
在子类中调用父类的带参数的构造函数时,需要显示地区调用。对于上面的程序,animal 类中加入构造函数
animal(int height,int weight)
{
cout<<"animal construct"<<endl;
}
在fish 类中调用方式
fish():animal(400,300)
{
cout<<"fish construct"<<endl;
}
注意:基类中的 private 成员不能被派生类访问,所以也不能被继承。public 定义的成员可以在任何地方被访问。protected定义的成员只能在该类以及其子类中被访问。
8、虚函数与多态性
先贴一段代码,再分析
#include <iostream>
using namespace std;
class animal
{
public:
void eat()
{
cout<<"animal eat"<<endl;
}
void sleep()
{
cout<<"animal sleep"<<endl;
}
void breathe()
{
cout<<"animal breathe"<<endl;
}
};
class fish:public animal
{
public:
void breathe()
{
cout<<"fish bubble"<<endl;
}
};
void fn(animal *pAn)
{
pAn->breathe();
}
void main()
{
animal *pAn;
fish fh;
pAn=&fh;
fn(pAn);
system("pause");
}
运行结果:animal breathe
分析:这句话中 :pAn=&ph 。fish对象也是 animal 对象,C++编译器会自动将fish 转换成 animal ,不用强制类型转换。当然,animal对象不能看成是 fish 对象。
fish 类的对象所占内存 = animal 的对象所占内存 + fish 对象自身增加的部分
将基类中的 breathe() 函数前加上 virtual 变成虚函数
virtual void breathe()//虚函数
{
cout<<"animal breathe"<<endl;
}
输出结果:fish bubble
多态性:在基类的函数前加上关键字 virtual ,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。C++的多态性由虚函数实现。在这个程序中,对象 pAn 的实际类型是 fish 类型,所以调用 fish 类中的 breathe()函数。
9、纯虚函数
纯虚函数是指被表明为不具体实现的虚成员函数。它可以让类先具有一个操作名称,而没有操作内容,让派生类在继承时再具体的给出定义。
将上面的 breathe() 函数声明为纯虚函数
virtual void breathe()=0;//纯虚函数
凡是含有纯虚函数的类都是抽象类。这种类不能声明对象,只是作为基类为派生类服务。在派生类中必须完全实现基类的纯虚函数,否则,派生类也变成抽象类,不能实例化对象。
注意:C++的多态性是由虚函数实现,不是纯虚函数。
10、函数的覆盖和隐藏
函数的覆盖:
在8中给出的程序中基类 animal 中的函数 breathe() ,和派生类 fish 中的函数 breathe() 函数名及参数列表都一样,发生了函数的覆盖。
函数覆盖的条件:基类函数必须是虚函数
发生覆盖的两个函数要分别位于派生类和基类中
函数名称和参数列表必须完全相同
函数的隐藏:
还是对于上面的例子分析,如果将基类 animal 中的 breathe() 函数前的 virtual 去掉,变成非虚函数,就发生了函数隐藏。
函数隐藏是指派生类中具有与基类同名的函数(参数列表是否相同不考虑),从而在派生类中隐藏了基类的同名函数。
注意:覆盖、隐藏、重载的区别
重载发生在同一个类中
基类派生类的函数名字和参数列表都相同,基类函数没有 virtual 关键字,就是隐藏,如果有,就是覆盖。
基类派生类的函数名字相同,列表不同,不管有没有 virual 关键字,都是隐藏。
想要调用被隐藏的基类函数,使用格式——类名::函数名(参数)
11、引用
引用就是一个变量的别名,必须在声明时初始化,而且一旦初始化后,就变成一块特定的内存,再也不能代表其他的内存。
引用和指针的区别:
引用的地址没有任何意义,就是它所引用的变量或对象的地址。
指针是地址,指针变量要存储地址值,所以要占用存储空间。
12、sizeof用法
sizeof有三种语法形式:
1) sizeof (object); //sizeof (对象)
2) sizeof object; //sizeof 对象
3) sizeof (type_name); //sizeof (类型)
对象可以是各种类型的变量,值为表达式的最终结果的数据类型的大小。
int i;
sizeof(int); //值为4
sizeof(i); //值为4,等价于sizeof(int)
sizeof i; //值为4
sizeof(2); //值为4,等价于sizeof(int),因为2的类型为int
sizeof(2 + 3.14); //值为8,等价于sizeof(double),因为此表达式的结果的类型为double
结构体的
sizeof
涉及到字节对齐问题,这样有助于加快计算机的取数速度,否则就得多花指令周期了。
struct S1
{
char a;
int b;
};
sizeof(S1); //值为8,字节对齐,在char之后会填充3个字节。
struct S2
{
int b;
char a;
};
sizeof(S2); //值为8,字节对齐,在char之后会填充3个字节。
struct S3
{
};
sizeof(S3); //值为1,空结构体也占内存。
整个联合体的
sizeof
也就是每个成员
sizeof
的最大值。
union u
{
int a;
float b;
double c;
char d;
};
sizeof(u); //值为8
数组的sizeof值等于数组所占用的内存字节数。
1)当字符数组表示字符串时,其sizeof值将’/0’计算进去。
2)当数组为形参时,其sizeof值相当于指针的sizeof值。
char a[10];
char n[] = "abc";
cout<<"char a[10]"<<sizeof(a)<<endl;//数组,值为10
cout<<"char n[] = /"abc/""<<sizeof(n)<<endl;//字符串数组,将'/0'计算进去,值为4
void func(char a[3])
{
int c = sizeof(a); //c = 4,因为这里a不在是数组类型,而是指针,相当于char *a。
}
指针是用来记录另一个对象的地址,所以指针的内存大小当然就等于计算机内部地址总线的宽度。在32位计算机中,一个指针变量的返回值必定是4。指针变量的sizeof值与指针所指的对象没有任何关系。
sizeof也可对一个函数调用求值,其结果是函数返回值类型的大小,函数并不会被调用。对函数求值的形式:sizeof(函数名(实参表))。
注意:1)不可以对返回值类型为空的函数求值。 2)不可以对函数名求值。 3)对有参数的函数,在用sizeof时,须写上实参表。
C++中 类的成员变量、静态成员是不占类的大小的。如果有虚函数,类的大小 +4 个字节,如果是虚继承,虚基类的大小不算,还要加上 4 个字节,用来指向虚基对象的指针。空类的大小为1。
class Base
{
public:
Base();
virtual ~Base(); //每个实例都有虚函数表
void set_num(int num) //普通成员函数,为各实例公有,不归入sizeof统计
{
a=num;
}
private:
int a; //占4字节
char *p; //4字节指针
};
class Derive:public Base
{
public:
Derive():Base(){};
~Derive(){};
private:
static int st; //非实例独占
int d; //占4字节
char *p; //4字节指针
};
int main()
{
cout<<sizeof(Base)<<endl; //12
cout<<sizeof(Derive)<<endl; //20
return 0;
}
Base类里的int a;char *p;占8个字节。而虚析构函数virtual ~Base();的指针占4子字节。其他成员函数不归入sizeof统计。
Derive类首先要具有Base类的部分,也就是占12字节。int d;char *p;占8字节,static int st;不归入sizeof统计所以一共是20字节。
纯手打,转载请注明出处:Summer_ZJU
边写边更新一点吧,先搬砖去了……