- 结构体与对象聚合
- 成员函数
- 基本概念
- 成员函数的声明与定义
- 成员函数与 this 指针
- 成员函数的名称查找与隐藏关系
- 静态成员函数
- 访问限定符与友元
结构体的声明与定义(注意定义后面要跟分号来表示结束)
#include#include using namespace std; // 定义一个结构体,x和y组合到一起 struct Str{ int x; int y; }; // 这个分号是必须的 struct Str; // 结构体的声明,告诉编译器这是一个结构体。 int main() { Str m_str; m_str.x = 3; cout << m_str.x << endl; }
仅有声明的结构体是不完全类型( incomplete type )
#include#include using namespace std; struct Str{ }; struct Str1; int main() { Str m_str; Str1 m_str1; // 报错 variable has incomplete type 'Str1' // 仅有声明的结构体是不完全类型( incomplete type ) }
结构体(以及类)的一处定义原则:翻译单元级别
变量在不同的编译单元都可以有不同的定义。结构体和类都是翻译单元级别(文件)的一处定义。
数据成员(数据域)的声明与初始化
( C++11 )数据成员可以使用 decltype 来声明其类型,但不能使用 auto
#include#include using namespace std; struct Str{ decltype(3) x; // 这里不能使用auto }; int main() { Str m_str; }
数据成员声明时可以引入 const 、引用等限定
#include#include using namespace std; struct Str{ const int x = 5; }; int main() { Str m_str; }
数据成员会在构造类对象时定义
类内成员初始化( C++11 )
#include#include using namespace std; struct Str{ int x = 5; // 类内成员初始化 }; int main() { Str m_str; cout << m_str.x << endl; // 5 }
聚合初始化:从初始化列表到指派初始化器
但是这里有个问题,就是这样的话,顺序一定要和Str里面变量声明的顺序相同,不然一定会出错。c++20使用了一个指派初始化器
mutable 限定符
#include#include using namespace std; struct Str{ mutable int x = 0; // mutable 可修改的 int y = 1; }; int main() { const Str m_str; m_str.x = 3; // 这样x就是可修改的了 }
静态数据成员 多个对象之间共享的数据成员
#include#include using namespace std; struct Str{ int x = 0; int y = 1; }; int main() { Str m_str1; Str m_str2; m_str1.x = 100; cout << m_str1.x << endl; // 100 cout << m_str2.x << endl; // 0 m_str1和m_str2是2个不同的对象,分别包含一组x和y }
但是如果我们把数据成员设置成静态成员,那么m_str1和m_str2的数据成员之间就可以共享了。
#include#include using namespace std; struct Str{ static int x; int y = 1; }; int Str::x; int main() { Str m_str1; Str m_str2; m_str1.x = 100; cout << m_str1.x << endl; // 100 cout << m_str2.x << endl; // 100 x是静态成员,2个对象之间的值共享了 }
C++98 :类外定义, const 静态成员的类内初始化
C++17 :内联静态成员的初始化
可以使用 auto 推导类型:
注意只有静态数据成员可以,一般的数据成员类型不支持auto。但是可以使用delctype。
静态数据成员的访问:
#include#include using namespace std; struct Str{ static int x; }; int Str::x = 3; int main() { Str m_str1; Str* ptr = &m_str1; cout << Str::x << endl; // 3 域操作符 cout << m_str1.x << endl; // 3 cout << ptr->x << endl; // 3 }
在类的内部声明相同类型的静态数据成员:
由于引入了成员函数,就把简单的结构体(聚集数据),变成了抽象数据类型。就是类。
#include#include using namespace std; struct Str{ int x = 3; // 成员函数 void fun() { cout << x << endl; } }; void fun(Str obj) { cout << obj.x << endl; } int main() { Str m_str; // fun(m_str); m_str.fun(); }
关键字 class,明确的表明是一个类
class Str{
public:
int x = 3;
// 成员函数
void fun()
{
cout << x << endl;
}
};
类本身形成域,称为类域。
成员函数的声明与定义类内定义(隐式内联)
#include#include using namespace std; class Str{ public: int x = 1; // 类内定义 void fun() { cout << x << endl; } }; int main() { Str m_str; m_str.x = 3; m_str.fun(); }
为了防止多个文件引用过一个类的成员函数,会产生重复定义。所以成员函数要设计成隐式内联的。
类内声明 + 类外定义
类外定义的函数,函数不是内联的。
类与编译期的两遍处理
成员函数与尾随返回类型( trail returning type )
#include#include #include "header.h" using namespace std; int main() { Str m_str; m_str.x = 5; cout << &m_str << endl; // 0x7ffee13066e8 m_str.fun(); // 0x7ffee13066e8,编译器会调用fun(&m_str) Str m_str2; m_str2.x = 10; cout << &m_str2 << endl; // 0x7ffee13066e0 m_str2.fun(); // 0x7ffee13066e0 }
#includeusing namespace std; class Str{ public: int x = 1; // 给fun一个隐藏的参数 Str* this,这个是Str第一个参数 void fun() { cout << this << endl; cout << this->x << endl; } };
可以通过this来显式访问类的成员数据
this是一个Str * const的指针,它指向了Str的对象。this本身不可修改,但是this指向的内容可以修改。有时候我们不想类外部的结构修改类内部的数据成员或者成员函数.
#include#include using namespace std; struct Str { inline static int x; // 静态数据成员 // 静态成员函数,不能在静态成员函数中使用成员函数x // fun会被共享,不会传入this。所以没法调用this->x // 为什么要引入静态成员函数 // 一般描述和类紧密相关的东西,同时不需要对象具体信息获取相应的结果 static void fun() { cout << x << endl; } }; int main() { Str m_str1; m_str1.fun(); Str::fun(); Str m_str2; }
成员函数基于引用限定符的重载( C++11 )
使用public/private/protected限定类成员的访问权限
#include#include using namespace std; struct Str { void fun() { // private内部可以访问 cout << x << endl; } // 私有的 private: // protected,外面也无法访问 // public: int x; int y; }; int main() { Str m_str; // 如果x是public,就可以访问 cout << m_str.x << endl; // error: 'x' is a private member of 'Str' }
struct默认是public,class默认是private。
访问权限的引入使得可以对抽象数据进行封装。比如我们买的电视,就是封装了很多线路。外面只有几个接口。
使用友元打破访问权限限制——关键字friend
声明某个函数是friend友元
#include#include using namespace std; int main(); class Str { // 通过friend告诉Str,main是Str的好朋友,可以访问它的private成员 friend int main(); inline static int x; }; int main() { Str m_str; cout << Str::x << endl; // 将main设置为友元 }
也可以声明某个类是类的friend
#include#include using namespace std; class Str2; class Str { // Str2是Str的友元,Str2的函数可以访问Str的private成员 friend Str2; inline static int x; }; class Str2 { void fun() { cout << Str::x << endl; } }; int main() { }
friend就是来打破封装的,但是要慎重使用friend。封装是很有用的,所以不要轻易的打破封装,会有风险。如果我们声明了friend,那么它就可以访问该类的所有成员,所有成员都暴露给这个朋友了。
friend放到哪个section都可以,不论是private,public或者protect都可以。
友元类内声明:
使用限定名称并非友元类的声明。
在类内无法定义友元类,只能是声明
在类外面定义友元函数,在类里面声明它。
在类的内部定义友元函数
隐藏友元:



