总结:成员变量是包含在每个对象中的,是占用对象字节的。而成员函数虽然也写在类的定义中,但成员函数不占对象字节数的(不占用内存空间)。
总结:对于类中

this指针调整:发生在多重继承中
派生类对象是包含基类子对象的。如果派生类只从一个基类继承,那么这个派生类对象的地址(首地址)和基类的首地址相同,如果派生类对象同时继承多个基类,那么第一个基类子对象的开始地址和派生对象的开始地址相同,后续这些基类子对象的开始地址和派生类对象的开始地址相差前边那些基类子对象所占用的内存空间
总结:调用哪个子类的成员函数,这个this指针就会被编译器自动调整到对象内存布局中对应该子类对象的起始地址那去。
#include
#include
class A
{
public:
int a;
A()
{
printf("A::A()的this指针是:%p\n", this);
}
void funcA()
{
printf("A::funcA()的this指针是:%p\n", this);
}
};
class B
{
public:
int b;
B()
{
printf("B::B()的this指针是:%p\n", this);
}
void funcB()
{
printf("B::funcB()的this指针是:%p\n", this);
}
};
class C : public A, public B
{
public:
int c;
C()
{
printf("C::C()的this指针是:%p\n", this);
}
void funcC()
{
printf("C::funcC()的this指针是:%p\n", this);
}
};
int main(int argc, char **argv)
{
std::cout << "sizeof(A) :" << sizeof(A) << std::endl;
std::cout << "sizeof(B) :" << sizeof(B) << std::endl;
std::cout << "sizeof(C) :" << sizeof(C) << std::endl;
std::cout << std::endl;
C objc;
std::cout << std::endl;
objc.funcA();
objc.funcB();
objc.funcC();
return 0;
}
输出结果为:
sizeof(A) :4
sizeof(B) :4
sizeof(C) :12
A::A()的this指针是:0x7ffd7c617dfc
B::B()的this指针是:0x7ffd7c617e00
C::C()的this指针是:0x7ffd7c617dfc
A::funcA()的this指针是:0x7ffd7c617dfc
B::funcB()的this指针是:0x7ffd7c617e00
C::funcC()的this指针是:0x7ffd7c617dfc

默认构造函数(缺省构造函数):没有参数的构造函数。传统认识认为如果我们自己没定义任何构造函数,
那么编辑器就会为我们隐式自动定义一个默认的构造函数,我们称这种构造函数为:合成的默认构造函数,
合成的默认构造函数只有在必要的时候编译器才会为我们合成出来,而不是必然或者必须为我们合成出来。
父类带缺省构造函数,子类没有任何构造函数,那因为父类这个缺省的构造函数要被调用,所以编译器会为这个子类合成出一个默认构造函数。
合成的目的是为了调用父类的构造函数。换句话说,编译器合成了默认的构造函数,并在其中安插代码调用其父类的缺省构造函数。
如果一个类含虚函数,但没有任何构造函数时,因为虚函数的存在,编译器会给我们生成一个基于该类的虚函数表vftable。编译器给我们合成一个构造函数,在其中安插代码,并把类的虚函数表地址赋给类对象的虚函数表指针,
我们可以把虚函数表指针看成是我们表面上看不见的一个类的成员函数。
当我们有自己的默认构造函数时,编译器会根据需要扩充我们自己写的构造函数代码,比如调用父类构造函数,给对象的虚函数表指针赋值。没有默认构造函数时必要情况下编译器帮我们
合成默认构造函数,如果有默认构造函数时,编译器会根据需要扩充默认构造函数。
如果一个类带有虚基类(通过2个直接基类继承一个间接基类,所以虚基类一般是三层结构),编译器也会为它合成一个默认构造函数。虚基类结构编译器为子类和父类都产生了合成的默认构造函数。

class A
{
public:
};
class B1: virtual public A
{
public:
};
class B2: virtual public A
{
public:
};
class C : public B1, public B2
{
public:
};
g++ -fdump-class-hierarchy -c main.cpp:可以输出详细的虚函数表内容
传统上大家认为如果我们没有定义一个自己的拷贝构造函数,编译器会帮助我们合成一个拷贝构造函数。这个合成的拷贝构造函数也是在必要的时候才会被编译器合成出来。
#include
#include
class B{
public:
int m_b;
};
class A{
public:
int m_a;
B b;
};
int main(int argc, char **argv)
{
A obja0;
obja0.m_a = 10;
obja0.b.m_b = 10;
A obja1 = obja0;// 调用拷贝构造函数,这个obja1.m_a=10;是编译器内部一个手法(成员变量初始化)
// 直接按值拷贝过去,编译器不需要合成拷贝构造函数
// obja1 = obja0; 是拷贝构造一个对象
// 我们没有写类A的拷贝构造函数,编译器也没有帮助我们生成拷贝构造函数。
// 我们却发现obja0对象的一些成员变量值确实被拷贝到obja1中去,这是编译器内部的一些直接拷贝数据的手法
// 比如类A中有类型B成员变量b,也会递归的去拷贝类B的每个成员变量。
return 0;
}
如果我们不写自己的拷贝构造函数,在以下情况下编译器会帮助我们合成出拷贝构造函数来。
如果一个类A没有拷贝构造函数,但是含有一个类类型B的成员变量,该类型B有拷贝构造函数,那么当代码中有涉及到类A的拷贝构造时,编译器会为A合成一个拷贝构造函数。
编译器合成的拷贝构造函数往往都是干一些特殊的事情,如果只是一些类成员变量的值拷贝这些事,编译器是不用专门合成出拷贝构造函数来干的。
#include
#include
class B{
public:
B(const B&) {
std::cout << "B copy construct done" << std::endl;
}
B(){
}
int m_b;
};
class A{
public:
int m_a;
B b; // 含有一个类类型B的成员变量b,且B类型含有拷贝构造函数
};
int main(int argc, char **argv)
{
A obja0;
obja0.m_a = 10;
obja0.b.m_b = 10;
A obja1 = obja0; // 调用拷贝构造函数
return 0;
}
如果一个类C没有拷贝构造函数,但是它有一个父类B,父类有拷贝构造函数,当代码中有涉及到C的拷贝构造时,编译器会为C合成一个拷贝构造函数,调用父类的拷贝构造函数。
#include
#include
class B{
public:
B(const B&) {
std::cout << "B copy construct done" << std::endl;
}
B(){
}
int m_b;
};
class C : public B{
public:
int m_c;
};
int main(int argc, char **argv)
{
C objc0;
C objc1 = objc0;
return 0;
}
如果一个类C没有拷贝构造函数,但是该类声明了或者继承了虚函数,当代码中有涉及到C的拷贝构造函数时,编译器会为C合成一个拷贝构造函数,往这个拷贝构造函数里插入语句:这个
语句的含义是设定类对象的虚函数表指针值。
声明虚函数
#include
#include
class C {
public:
int m_c;
virtual void vfunc() {
std::cout << "virtual function call" << std::endl;
}
};
int main(int argc, char **argv)
{
C objc0;
C objc1 = objc0;
return 0;
}
继承虚函数
#include
#include
class B{
public:
B(const B&) {
std::cout << "B copy construct done" << std::endl;
}
B(){
}
virtual void vfunc() {
std::cout << "virtual function call" << std::endl;
}
int m_b;
};
class C : public B{
public:
int m_c;
};
int main(int argc, char **argv)
{
C objc0;
C objc1 = objc0;
return 0;
}
如果一个类C没有拷贝构造函数,但是该类含有虚基类,当代码中有涉及到类C的拷贝构造时,编译器会为该类生成一个拷贝构造函数。
#include
#include
class A{
public:
};
// 虚继承
class B1: virtual public A {
public:
};
class B2: virtual public A {
public:
};
class C: public B1, public B2 {
public:
};
int main(int argc, char **argv)
{
C objc0;
C objc1 = objc0;
return 0;
}
class A {
public:
int &m_val0; // 成员是引用
A(int &val):m_val0(val)
};
class A {
public:
int &m_val0;
const int m_val1; // 成员是const 类型
A(int &val):m_val0(val),m_val1(val)
};
class B{
public:
int m_a;
int m_b;
B(int a, int b);
};
class A : public B{
public:
int &m_val0;
const int m_val1;
A(int &val):m_val0(val),m_val1(val),B(val,val)
};
class C{
public:
int m_a;
C(int a);
};
class A{
public:
int &m_val0;
const int m_val1;
C c_c;
A(int &val):m_val0(val),m_val1(val),c_c(val)
};
使用初始化列表能够提高程序的运行效率。
#include
#include
#include
#include
class C{
public:
int m_a;
C(int val=0):m_a(val)
{
printf("this=%p ", this);
printf("C(int)构造函数被调用\n");
}
C(const C &val)
{
printf("this=%p ", this);
printf("C拷贝构造函数被调用\n");
}
C &operator=(const C &val)
{
printf("this=%p ", this);
printf("C拷贝赋值运算符被调用\n");
return *this;
}
~C()
{
printf("this=%p ", this);
printf("C析构函数被调用\n");
}
};
class A{
public:
C c_c;
int m_val;
A(int val) // 这里构造了c_c,耗费一次构造函数调用
{
c_c = 1000; // 构造一个临时对像,把临时对下内容赋值给c_c,释放掉临时对象
m_val = val;
}
};
int main(int argc, char **argv)
{
A obj(1000);
return 0;
}
输出结果为:
this=0x7ffff2950d10 C(int)构造函数被调用
this=0x7ffff2950cd4 C(int)构造函数被调用
this=0x7ffff2950d10 C拷贝赋值运算符被调用
this=0x7ffff2950cd4 C析构函数被调用
this=0x7ffff2950d10 C析构函数被调用
gdb调试现象如下:
(gdb) b main.cpp:49
Breakpoint 1 at 0x120c: file main.cpp, line 49.
(gdb) r
Starting program: /home/xiaxin/workspace/cplus/main
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, main (argc=1, argv=0x7fffffffdd48) at main.cpp:49
49 A obj(1000);
(gdb) s
A::A (this=0x7fffffffdc10, val=1000) at main.cpp:40
40 A(int val)
(gdb) n
41 {
(gdb) n
this=0x7fffffffdc10 C(int)构造函数被调用
42 c_c = 1000;
(gdb) n
this=0x7fffffffdbd4 C(int)构造函数被调用
this=0x7fffffffdc10 C拷贝赋值运算符被调用
this=0x7fffffffdbd4 C析构函数被调用
43 m_val = val;
(gdb)
#include
#include
#include
#include
class C{
public:
int m_a;
C(int val=0):m_a(val)
{
printf("this=%p ", this);
printf("C(int)构造函数被调用\n");
}
C(const C &val)
{
printf("this=%p ", this);
printf("C拷贝构造函数被调用\n");
}
C &operator=(const C &val)
{
printf("this=%p ", this);
printf("C拷贝赋值运算符被调用\n");
return *this;
}
~C()
{
printf("this=%p ", this);
printf("C析构函数被调用\n");
}
};
class A{
public:
C c_c;
int m_val;
A(int val):c_c(1000) // 初始化列表调用
{
m_val = val;
}
};
int main(int argc, char **argv)
{
A obj(1000);
return 0;
}
输出结果为:
this=0x7ffe14386950 C(int)构造函数被调用
this=0x7ffe14386950 C析构函数被调用