一、类对象作为类成员
1.概念
类对象作为类成员也就是子类和父类的关系。当其他类作为本类成员的时候,先构造其他类对象,再构造自身;析构的顺序为:先析构自身,再析构其他类对象。
2.举例
#include<iostream>
#include<string>
using namespace std;
class Phone {
public:
Phone(string name) {
m_pname = name;
}
string m_pname;
};
class Person {
public:
Person(string name, string pname) :m_name(name), m_phone(pname) {
}
string m_name;
Phone m_phone;
};
void text01() {
Person p("张三", "三星");
cout << p.m_name << p.m_phone.m_pname << endl;
}
int main() {
text01();
return 0;
}
在上例中,Phone类作为Person类的成员,也即Phone类是Person类的一个子类。编译器先构造Phone类,再构造Person类。可以通过在两个类的构造函数和析构函数中添加一些输出语句简单验证。
二、静态成员
1.概念
静态成员就是在成员变量和成员函数前面加上关键字static,成为静态成员。静态成员分为:
静态成员变量
1.所有对象共享一份数据
2.在编译阶段分配内存(在全局区)
3.类内声明,类外初始化
class Person {
public:
static int m_a; //类内声明
};
int Person::m_a = 100; //类外初始化,要写清是Person作用域下的变量
静态成员函数
1.所有对象共享同一个函数
2.静态成员函数只能访问静态成员变量
class Person {
public:
static void func() {
m_a = 100;
//m_b = 100; //错误,不能访问非静态成员变量
}
static int m_a; //类内声明
int m_b; //非静态成员变量
};
2.调用
静态成员有两种访问方式:
void text(){
//1.通过对象访问
Person p1;
p1.func();
//2.通过类名访问
Person::func();
}
三、C++对象模型和this指针
1.对象模型
成员变量和成员函数分开存储,只有非静态成员变量才属于类的对象上。
空对象占用内存空间为1(可以创建一个空类,用sizeof计算内存大小),因为c++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占内存的位置,每个空对象也应该有一个独一无二的内存地址。
只有非静态成员变量属于类的对象上,其余“静态成员变量”,“非静态成员函数”,“静态成员函数”等均不属于类对象上。可以通过在一个类中添加不同的成员变量和成员函数,用sizeof分别计算相应的值验证。
2.this指针
概念:this指针指向被调用的成员函数所属的对象。this指针不需要定义,直接使用即可。
用途1.当形参和成员变量同名时,可用this指针来区分;
#include <iostream>
using namespace std;
class Person {
public:
Person(int age) {
this->age = age;
}
int age;
};
void text01() {
Person p(18);
cout << p.age << endl;
}
int main() {
text01();
return 0;
}
在Person成员函数中,使用this->age指向的是在public中定义的age。也即this指针指向被调用的成员函数所属的对象。
用途2.在类的非静态成员函数中返回对象本身,可使用 return * this 返回。
#include <iostream>
using namespace std;
class Person {
public:
Person(int age) {
this->age = age;
}
Person& PersonAddAge(Person &p) {
this->age += p.age;
return *this;
}
int age;
};
void text02() {
Person p1(3);
Person p2(5);
p2.PersonAddAge(p1).PersonAddAge(p1);
cout << "p1的年龄为:" << p1.age << endl; //3
cout << "p2的年龄为:" << p2.age << endl; //5+3+3=11
}
int main() {
text02();
return 0;
}
有疑问的代码可能是 p2.PersonAddAge(p1).PersonAddAge(p1); 可能会心想,它怎么加了这么长?这是因为我们返回的是对象本身,当我们调用PersonAddAge函数的时候,this指针指向p2,这时候返回 *this,那么返回的就是 p2 本体,相应的函数返回类型我们也修改为 Person&(Person的引用)。这样 p2.PersonAddAge(p1) 就相当于 p2,也就自然可以往后继续追加。这称作“链式编程思想”。
为什么返回 Person& 而不是 Person,即为什么返回引用类型而不是值类型?
原因是如果返回值,在执行 p2.PersonAddAge(p1) 的时候就会调用拷贝构造函数(拷贝构造函数的调用时机,可以参考我的另一篇博客,附在文章末尾),那么这时候返回的 p2 就不是原来的 p2,而是原来 p2 的一个复制体,我们称作 p2',再往后追加,p2.PersonAddAge(p1).PersonAddAge(p1) 返回的就是p2''。那么这时候我们输出 p2 的话,就是“5+3”即返回 8 。