一,const的作用
1,定义只读变量 即常量
2,修饰函数的参数和返回值
3,修饰类的成员函数,被const修饰的成员函数代表不修改成员变量的值
二,指针和引用的区别
1,引用是变量的一个别名,内部是只读指针
2,引用只能在初始化时赋值,指针可以在任何时候赋值
3,引用不能为null,指针可以为null
4,引用变量内存单元保存的是被引用变量的地址
5,"sizeof 引用" 等于指向变量的大小,"sizeof 指针"等于指针本身的大小
6,引用可以取地址操作,返回的是被引用变量本身所在的内存单元地址
7,引用使用在源代码级 相当于普通变量一样使用,做函数参数时,内部传递的实际是变量地址
三,c++中有了malloc /free,为什么还需要new/delete
1,malloc 与free是c++/c语言的标准库函数。new/delete是C++的运算符,他们都可以用于申请动态内存和释放内存
2,对于非内部数据类型的对象而言,malloc/free不能满足动态对象的要求
对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数
由于malloc/free是库函数不是运算符,不在编译器控制权限之内,不能够把执行构造函数和西沟函数的任 务强加于malloc/free
3,因此c++语言需要一个能完成动态分配和初始化工作的运算符new,和衣蛾能完成清理与释放内存工作的运算符delete。注意new /delete不是库函数。
四,编写String 的构造函数,析构函数,拷贝构造函数和赋值函数
class String
{
public:
String(const char *str = NULL); //普通构造函数
String(const String &other); //拷贝构造函数
~String(void);//析构函数
String &operate =(const String &other) ;//赋值函数
private:
char *m_data;//用于保存字符串
};
解答:
//构造函数
String::String(const char* str)
{
if (str == NULL)
{
m_data = new char[1]; //得分点:对空字符串自动申请存放结束标志'\0'的空间
*m_data = '\0';
}
else
{
int length = strlen(str)
m_data = new char[length+1]
strcpy(m_data,str)
}
}
//析构函数
String::~String(void)
{
delete[] m_data;或者 delete m_data //释放空间
}
//拷贝构造函数
String::String(const String &other)
{
int length = strlen(other.m_data);
m_data = new char[length+1];
strcpy(m_data,other.m_data);
}
//赋值函数
String& String::operate =(const String &other)
{
if (this == &other) //检查自赋值
{
return this;
}
delete [] m_data ; //释放原有内存
int length = strlen(other.m_data);
m_data = new char[length+1];
strcpy(m_data,other.m_data);
return *this //返回对象本身的引用
}
六,多态的实现
c++的多态性 用一句话概括就是:在基类的函数前加virtual关键字,在派生类中重写改函数,运行时将会根据 对象的实际类型来调用响应的函数。如果对象的类型是派生类,就调用派生类的函数,如果对象类型是基类,就调用基类的函数
再看看以下几点:
1,虚函数:用virtual 关键字神明的函数,虚函数肯定是类的成员函数
2,存在虚函数的类都有一个一维的虚函数表叫做虚表,类的对象有一个指向虚表开始的虚指针,虚表是和类对应的,虚表指针是和对象对应的
3,多态性是一个接口多种实现,是面向对象的核心,分为类的多态性和函数的多态性
4,多态用虚函数来实现,结合动态绑定
5,纯虚函数是虚函数再加上 = 0,
6,抽象类是指 包括之色一个纯虚函数的类
纯虚函数:virtual void breathe() = 0 ;几抽象类,必须在子类实现这个函数,即先有名称,没有内容,在派生类实现内容。
示例代码:
class animal
{
public :
void sleep()
{
cout<<"animal sleep"<<endl;
}
void breathe()
{
cout <<"animal breathe"<<endl;
}
};
class fish :public animal
{
public :
void breathe()
{
cout << "fish breathe" << endl;
}
};
void main()
{
fish fh;
animal *pAn = &fh; //隐式类型转换
pAn->breathe();
}
程序中没有定义虚函数,执行结果是 : animal breathe
我们在main()函数中首先定义了一个fish类的对象fh,接着定义了一个指向animal类的指针变量pAn,将fh的地址赋给了指针变量pAn,然后利用该变量调用pAn->breathe()。
往往将这种情况和C++的多态性搞混淆,认为fh实际上是fish类的对象,应该是调用fish类的breathe(),输出“fish bubble”,然后结果却不是这样。
下面我们从两个方面来讲述原因。
1,编译的角度
C++编译器在编译的时候,要确定每个对象调用的函数(要求此函属实非虚函数)的地址,这成为早期绑定(early binding),当我们将fish类的对象fh赋给pAn时, C++编译器进行了类型转换,此时c++编译器认为变量pAn保存的就是animal对象的地址。当在main()函数中执行pAn->breathe()时,调用的就是animal对象的breathe函数
2,内存模型的角度
我们给出了fish对象内存模型,如下图
我们构造fish对象时,首先调用animal类的构造函数去构造animal类对象,然后才调用fish类的构造函数,完成自身部分的构造,从而拼接处一个完整的fish对象。
当我们将fish类的对象转换为animal类型时,该对象就被认为十元对象整个内存模型得到上半部分,就上上图的"animal的对象所占内存"。
那么多当我们利用类型转换后的对象指针去调用他的方法时,当然也就是调用它所在的内存中的放大,因此输出 animal breathe
在例1-1的程序中,我们知道pAn实际指向的是fish类的对象,我们希望输出的结果是鱼的呼吸方法,即调用fish类的breathe方法。这个时候,就该轮到虚函数登场了。