C++中,实例化一个对象,表示请求编译器分配一块固定大小的内存,大小为sizeof(object),那么问题来了,这个对象的地址&object是什么呢?
以下从6个方面考虑对象地址,测试环境:gcc编译器
1、普通类class中包含了成员函数和成员变量,对象object的地址即为该对象内所包含的第一个字段的地址。
这个没啥好说的,类似于数组地址与数组首元素地址、结构体变量的地址与其第一个字段的地址。
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
Person(string Name = "Tom", int Age = 10) /*缺省的default ctor*/
:name(Name),age(Age)
{}
void getAddr() const
{
cout << "object's this addr: " << this << endl;
cout << "object's first elem: " << &(this->name) << endl;
}
private:
string name;
int age;
};
int main()
{
Person p;
cout << "The object addr: " << &p << endl;
p.getAddr();
//cout << "hello...\n";
return 0;
}
执行结果: 对象p的地址就是对象p内首个字段name的地址。
XXX:~/Exercise$ g++ objectAddr.cpp
XXX:~/Exercise$ ./a.out
The object addr : 0x7fffe857ac40
object's this addr: 0x7fffe857ac40
object's first elem: 0x7fffe857ac40
2、那若是类class为空呢,类中什么都没有,那么一个空的类实例化一个对象object,此时对象大小等于几?它的地址又是什么呢?
逻辑上对象的大小等于0,但实际情况下它等于1,因为C++裁定凡是独立对象必须有非零大小,既然有大小就说明编译器分配了内存,有了内存肯定就有地址啦,如下:
#include <iostream>
#include <string>
using namespace std;
class Student
{};
int main()
{
Student s;
cout << "The object size : " << sizeof(s) << endl; /* sizeof(Student)也等于1 */
cout << "The object addr : " << &s << endl;
return 0;
}
执行结果:可以看到对象s大小为1,这是因为编译器为空对象内强行安插了一个char,所以s大小为1,(《Effective C++》条例39),而对象地址应该就是这个char的地址。
XXX:~/Exercise$ g++ objectAddr.cpp
XXX:~/Exercise$ ./a.out
The object size : 1
The object addr : 0x7ffd7e04dbbf
3、若一个类中包含了virtual虚函数,那么具现出一个实体object的地址是什么呢,此时应该是虚指针vptr的地址。
带有虚函数的类中,编译期间,在每一个class object中,一个额外的虚指针vptr会被编译器合成出来放在对象的开头,内含相关虚表vtbl的地址。
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
Person(string Name = "Tom", int Age = 10)
:name(Name),age(Age)
{}
virtual ~Person() {}
void getAddr() const
{
cout << "object's this addr: " << this << endl;
cout << "object's first elem: " << &(this->name) << endl;
cout << "object's second elem:" << &(this->age) << endl;
}
private:
string name;
int age;
};
int main()
{
Person p;
cout << "The object addr : " << &p << endl;
p.getAddr();
//cout << "hello...\n";
return 0;
}
执行结果:可以看出对象p的地址与p内首个字段name的地址不一样了,&p是对象内虚指针的地址,该地址向后偏移才是首个字段name的地址。(参考《Inside The C++ Object Model》)
XXX:~/Exercise$ g++ objectAddr.cpp
XXX:~/Exercise$ ./a.out
The object addr : 0x7fffddf9e860
object's this addr: 0x7fffddf9e860
object's first elem: 0x7fffddf9e868
object's second elem:0x7fffddf9e870
这里第一个字段name的地址与第二个字段age的地址相差8,为什么呢?因为string内有一根指针,在gcc编译器中,指针占8个字节。
4、若是在继承体系中,一个派生类对象的地址会是什么?
派生类对象object的组成包含了基类的成员变量和派生类自己新增的成员变量,该对象的地址是派生类对象中所包含的基类成员中的首个字段的地址,而不是派生类自己新增的第一个字段成员的地址。
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
Person(string Name = "Tom", int Age = 10)
:name(Name),age(Age)
{}
protected:
string name;
int age;
};
class Student : public Person
{
public:
Student(string Name = "Tom", int Age = 10, int Grade = 100)
:Person(Name,Age),grade(Grade)
{}
void getAddr() const
{
cout << "object's this addr: " << this << endl;
cout << "object's first elem: " << &(this->name) << endl;
cout << "object's second elem:" << &(this->age) << endl;
cout << "object's third elem: " << &(this->grade) << endl;
}
private:
int grade;
};
int main()
{
Student s;
cout << "The object addr : " << &s << endl;
s.getAddr();
//cout << "hello...\n";
return 0;
}
执行结果:对象s的地址即为对象s中首个字段name的地址,这里注意新增字段grade的地址,可以看出三个成员变量所占内存是连续的,但在一个对象中,继承自基类的部分和派生类自定义的部分不一定是连续存储的。(《C++ Primer》15.2.2)
XXX:~/Exercise$ g++ objectAddr.cpp
XXX:~/Exercise$ ./a.out
The object addr : 0x7ffe6b3f5be0
object's this addr: 0x7ffe6b3f5be0
object's first elem: 0x7ffe6b3f5be0
object's second elem:0x7ffe6b3f5be8
object's third elem: 0x7ffe6b3f5bec
5、若在继承体系中加入了虚函数,派生类对象的地址又是什么?
该地址是派生类对象object虚指针vptr的地址。
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
Person(string Name = "Tom", int Age = 10)
:name(Name),age(Age)
{}
virtual ~Person() {}
protected:
string name;
int age;
};
class Student : public Person
{
public:
Student(string Name = "Tom", int Age = 10, int Grade = 100)
:Person(Name,Age),grade(Grade)
{}
virtual ~Student() {}
void getAddr() const
{
cout << "object's this addr: " << this << endl;
cout << "object's first elem: " << &(this->name) << endl;
cout << "object's second elem:" << &(this->age) << endl;
cout << "object's third elem: " << &(this->grade) << endl;
}
private:
int grade;
};
int main()
{
Student s;
cout << "The object addr : " << &s << endl;
s.getAddr();
//cout << "hello...\n";
return 0;
}
执行结果:此时对象s的地址是虚指针vptr的地址,与第三个差不多,但是这个虚指针vptr是派生类对象自己的,而不属于派生类对象包含的基类成员的那部分,
若是具现一个Person实体 Person p; 这里p的地址才是基类对象自己的专属的虚指针的地址,多态性的体现正是靠这根虚指针vptr。
用一句话表示多态性: (*(this->vptr)[n])(this)
XXX:~/Exercise$ g++ objectAddr.cpp
XXX:~/Exercise$ ./a.out
The object addr : 0x7ffe61d5fbd0
object's this addr: 0x7ffe61d5fbd0
object's first elem: 0x7ffe61d5fbd8
object's second elem:0x7ffe61d5fbe0
object's third elem: 0x7ffe61d5fbe4
6、继承抽象基类的派生对象地址?
这个地址肯定就是派生类对象object自己的虚指针vptr的地址,因抽象基类不可具现实体。
7、若类中包含静态成员变量,该静态成员变量是属于类的,其存储在进程空间中的全局区,由该类的所有对象共享,静态成员变量不存储在对象本身占用的空间内。
注:对象内存大小组成如下:
(1)基本字段(成员变量);
(2)对齐;
(3)由vritual带来的额外的负担,例如虚指针vptr。
若有说错的,请走过路过的大佬们给个建议,谢谢。