OO 杂项
类的内存模型
What does C++ Object Layout Look Like? | Nimrod’s Coding Lab
空类默认提供的方法
class T {};
class T {
T(); // 构造函数
T(const T&); // 拷贝构造
T(const T&&); // 拷贝构造 rval
~T(); // 析构函数
T& operator=(const T&); // 拷贝赋值
T& operator=(const T&&); // 拷贝赋值 rval
T *operator &(); // 取地址
const T* operator &() const; // 取地址 const 重载版
};
最后两个函数比较怪。一个是返回非 const 的指针,另一个返回 const 的。在调用时,编译器如何决定调用哪个呢?
注意函数后的 const 修饰符。这两个函数实际应该长这样:
T *operator &(T *this);
const T* operator &(const T *this);
是 this 指针的功劳。调用方是哪种就执行哪个。
const 与 static 成员
static const 成员变量
对于成员变量,变量会在编译期确定,并被放到程序静态区。运行时,这个成员和类与对象在内存上没有关系,但是要通过类名来访问。
static const 只能修饰成员变量。在成员函数前面加 static const 的意义是对函数修饰 static,对函数返回值修饰 const。
static 成员
static 成员属于类,和对象没关系。
初始化
初始化需要在类的外部。因为在类的内部初始化实际上是对象的行为,而 static 变量是与对象无关的。
class Test{
public:
static a = 1; // not allowed
static constexpr int a = 1; // C++ 17
};
int Test::a = 1; // okay
使用与修改
对于 private 的 static 变量,除了初始化,在类外无法访问。
对 public 的变量,加上类限定符就可以访问了。
const 成员
对于 const 成员变量没什么好说的,const 起到限定无法修改的作用。
对 const 成员函数就有意思了。不过严格来说,就不该叫“const 成员函数”。
有些成员函数不会对对象的属性进行修改,对这类函数可以在其声明后加上 const 限制。
class Test{
public:
void get() const;
}
其原理很简单,就是将成员函数的 this 指针加上 const 限定:
void get(); <=> void get(Test *this);
void get() const; <=> void get(const Test *this);
友元
做朋友。破坏类原有的封装性,使外部能够在尽可能少的改动下,从一个类的外部访问这个类的私有成员。
友元类
A 指定 类B 是朋友。B 中就可以随意访问 A 的成员了。
注意,友元类要处理声明顺序的问题,看代码第一行。
class B; // A 声明友元需要 B 的定义,B 也需要访问 A 的成员。所以先把 B 声明在这里,方便 A 使用。
class A {
friend class B;
private:
int x;
static int y;
void foo();
};
class B {
A a;
void f() {
a.x;
A::y;
foo();
}
};
友元函数
无论哪种友元函数,都不是这个类的成员函数!!!
首先是友元成员函数。类 A 和 类 B 的一个成员函数 foo 做朋友。函数内部能随意访问 A 的私有成员,但函数之外都不行。
注意几点:
- 依赖顺序,需要前向声明
- foo 的参数只能是 A 的指针或者引用,因为此时 A 只是有了个名字,不知道占空间大小
- friend 定义友元函数语句中,函数前要加类限定符
class A;
class B{
void foo(A &a) {
a.x; // okay
}
void f(A &a) {
a.x; // invalid
}
}
class A{
friend void B::foo(A &a);
int x;
};
然后是友元全局函数。类 A 和外部函数 foo 做朋友。foo 能访问 A 所有东西。
- 依赖顺序随意。当然,如果函数参数有类的话还是要先声明,如代码中的例子
- 函数参数包含类对象时,可以不为指针和引用
- 多用于实现类运算符重载
class A{
friend void foo(A a);
};
void foo(A a) {
}