面向对象和面向过程常见问题
c++中对象内存分配:
class CGoods
{
private:
char _name[21];
int _amount;
float _price;
float _total_price;
public:
void RegisterGoods(const char[],float price,float total_price);
void CountTotal();
void GetName();
int GetAmount();
float GetPrice();
float GetTotalvalue() const;
};
每个对象有自己的数据区,但是函数的成员方法共享存储区域。
问题1:C++为了节省空间,将各个对象的成员方法存放到公用代码区,那么C++是如何区别每个函数属于不同的对象呢?
答案:引入this指针。
首先来看一段代码:
#include<iostream>
using namespace std;
class CGoods
{
private:
char _name[21];
int _amount;
float _price;
float _total_price;
public:
void RegisterGoods(const char[],float price,float total_price);
void CountTotal();
void GetName();
int GetAmount();
float GetPrice();
float GetTotalvalue() const;
};
float CGoods::GetTotalvalue()const
{
}
int main()
{
CGoods c1;
c1.RegisterGoods("C++",19,88.88);
//等价于:RegisterGoods(&c1,"C++",19,88.88);
CGoods c2;
c2.RegisterGoods("JAVA",23,99.9);
//等价于:RegisterGoods(&c2,"JAVA",23,99.9);
CGoods c3;
c3.RegisterGoods("Python",21,79.8);
//等价于:RegisterGoods(&c3,"Python",21,79.8);
return 0;
}
C++中,当使用对象 + . 调用成员函数时,实际上编译器会隐式的传递对象的地址作为第一个参数,这个地址也是this指针的地址。依靠这个,与其他对象调用的成员函数区别。
问题2:this指针是在什么时期添加的?
回答:this指针是在编译时期添加的。
问题3:p1 p2 p3 在栈上分配空间,那么他们在分配的时候,在这个空间上有没有对象?
class Pointer
{
public:
Pointer()
{
row = 0;
col = 0;
}
Pointer(int r, int c)
{
row = r;
col = c;
}
// Pointer(int r = 0, int c = 0)
// {
// row = r;
// col = c;
// }
//Pointer p4; //存在二义性,不知道是调用无参构造还是带有默认参数的构造函数
private:
int row;
int col;
};
int main()
{
Pointer p1; //构造无参的构造函数
Pointer p2(10,20); //构造含有参数的构造函数
Pointer p3(); //不可这样使用,函数声明 int fun()
//问题:在程序执行时,分配空间,代码区,数据区,堆区,栈区.给主函数分配栈帧,系统给对象分配空间.
//p1 p2 p3 在栈上分配空间,在这个空间上有没有对象?
//| p1 |
//| p2 |
//| p3 |
//没有,必须调动对象所属的构造函数来创建对象.并且给对象的成员进行初始化.
//编译器在编译链接过程结束后,调用时会为p1,p2分配8个字节.仅仅是分配空间,满足使用
//并不存放对象,在对象调用构造时,才会将对象存放在内存中.
return 0;
}
问题4:什么是析构函数
当定义一个对象的时候,C++会自动调用构造函数建立对象并且进行初始化,那么当一个对象的声明周期结束时,C++也会自动调用一个函数注销该函数,那么这个特殊的成员函数即为析构函数。
特点:
- 析构函数名和类名一样,~ + 类名
- 析构函数没有返回类型,且没有参数
- 一个类只有一个析构函数,但可以有多个构造函数
- 对象注销时,系统自动调用析构函数
- 如果没有显示的析构函数,系统会构造一个隐式的析构函数
问题5:构造函数和析构函数返回值
构造函数与析构函数是没有返回值的,但是我认为构造函数有隐含的返回值(this指针)。构造函数是在类的对象产生时自动调用的,构造函数被调用也就意味着产生了一个对象,而this指针是与对象实体相关联的。
class Pointer
{
public:
Pointer(int var)
{
_var = var;
cout << "_var:" << endl;
}
private:
int _var;
};
int main()
{
Pointer p1(10);
return 0;
}
当对象被实例化的时候,构造函数会返回一个首地址,系统先会申请一块地址空间用来存放对象,随后这个对象的地址会赋值给this指针。详细情况在问题10有说明。
问题6:new对象的两个方法
1、运算符调用
2、构建new
Pointer g_pt (12,23); //全局对象
//进入主函数之前,调用构造函数创建全局对象
int main()
{
Pointer p1(100,200);
Pointer *p = NULL;
p = new Pointer(10,20);
//new 的两个用法,运算符调用
//new 做的三件事情
//1 申请空间:malloc从堆区申请空间
//2 构建对象:在空间构造对象
//3 返回地址:将构造对象的地址返回
//构建new,对s指向的空间中,调用构造函数来构建对象
Pointer *s = (Pointer *)malloc(sizeof(Pointer));
//上述代码空间中是否有对象? 无
new(s) Pointer(12,23);
//深入理解:对象和空间的关系
//有对象必须有空间
//有空间不一定有对象
s->~Pointer(); //s调动析构函数,释放对象的资源,并没有释放空间
free(s); //释放空间
s = NULL;
delete(p);
//delete 做的三件事情
//1 调用析构
//2 释放空间
}
问题7:new 和 delete
new 做的三件事情:
1、 申请空间:malloc从堆区申请空间
2 、构建对象:在空间构造对象
3 、返回地址:将构造对象的地址返回
delete 做的两件事情:
1 、调用析构
2 、释放空间
问题8:构造函数构造顺序
//一个问题
class Object
{
private:
int val;
public:
Object(int x)
{
val = x;
cout << "create :" << val << endl;
}
};
Object o1(1);
int main()
{
Object o2(2);
}
Object o3(3);
输出结果:
1 3 2
对于输出结果的详细分析请见 3、C++构造和析构
问题9:C++中一切皆对象思想
class Object
{
private:
int sum;
int num;
public:
Object(int x = 0, int y = 0): sum(x),num(y){}
};
//C++中重要思想,一切皆对象
int main()
{
int a = 10;
int b(10);
int c = int (10); //伪构造函数
//上述过程与下面类似
Object obj;
Object obj(10);
Object obj2 = obj;
int *c = new int(20);
}
问题10:this指针传递问题
this调用,this指针在c++中存放在ecx或者cx寄存器进行传递。
A*const register this;
通过C++编译生成反汇编如下:
1、成员函数的THIS指针如何进行传递:
如果是使用对象 .调动成员方法时,逻辑上是面向对象,但是物理上还是面向过程。会将对象的地址传递给成员函数的this指针。
2、对象的地址传递:
对于c++来说,使用thiscall调用约定,直接进行传递,lea ecx , [c1]传递。我们将对象的地址获取,到达成员函数内部时,我们通过mov指令将ecx寄存器的值传递给this指针。
3、什么时候使用入栈的形式调用参数:
我们可以改写成C的调用约定,我们在函数前面添加__cdecl。通过lea指令将c1的地址传递给ecx寄存器。使用push eax直接传给this指针。