结构体的声明与定义(注意定义后面要跟分号来表示结束)
#include <iostream>
#include <vector>
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 <iostream>
#include <vector>
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 <iostream>
#include <vector>
using namespace std;
struct Str{
decltype(3) x;
// 这里不能使用auto
};
int main()
{
Str m_str;
}
数据成员声明时可以引入 const 、引用等限定
#include <iostream>
#include <vector>
using namespace std;
struct Str{
const int x = 5;
};
int main()
{
Str m_str;
}
数据成员会在构造类对象时定义
类内成员初始化( C++11 )
#include <iostream>
#include <vector>
using namespace std;
struct Str{
int x = 5; // 类内成员初始化
};
int main()
{
Str m_str;
cout << m_str.x << endl; // 5
}
聚合初始化:从初始化列表到指派初始化器
但是这里有个问题,就是这样的话,顺序一定要和Str里面变量声明的顺序相同,不然一定会出错。c++20使用了一个指派初始化器
mutable 限定符
#include <iostream>
#include <vector>
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 <iostream>
#include <vector>
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 <iostream>
#include <vector>
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 <iostream>
#include <vector>
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 <iostream>
#include <vector>
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 <iostream>
#include <vector>
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 <iostream>
#include <vector>
#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
}
#include <iostream>
using 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 <iostream>
#include <vector>
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 <iostream>
#include <vector>
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 <iostream>
#include <vector>
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 <iostream>
#include <vector>
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都可以。
友元类内声明:
使用限定名称并非友元类的声明。
在类内无法定义友元类,只能是声明
在类外面定义友元函数,在类里面声明它。
在类的内部定义友元函数
隐藏友元: