C++ 类和对象学习 —— 继承

2023-05-16

1.6 继承

利用继承技术,可以减少重复代码

1.6.1 继承的基本语法

普通实现

#include <iostream>
#include <string>
using namespace std;
//普通实现页面
// Java 页面
class Java
{
public:
	void header()
	{
		cout << "首页、公开课、登陆、注册……(公共头部)" << endl;
	}
	void footer()
	{
		cout << "帮助中心、交流合作、站内地图……(公共底部)" << endl;
	}
	void left()
	{
		cout << "Java、Python、C++、……(公共分类列表)" << endl;
	}
	void content()
	{
		cout << "Java 学科视频" << endl;
	}
};
// Python 页面
class Python
{
public:
	void header()
	{
		cout << "首页、公开课、登陆、注册……(公共头部)" << endl;
	}
	void footer()
	{
		cout << "帮助中心、交流合作、站内地图……(公共底部)" << endl;
	}
	void left()
	{
		cout << "Java、Python、C++、……(公共分类列表)" << endl;
	}
	void content()
	{
		cout << "Python 学科视频" << endl;
	}
};
// C++ 页面
class CPP
{
public:
	void header()
	{
		cout << "首页、公开课、登陆、注册……(公共头部)" << endl;
	}
	void footer()
	{
		cout << "帮助中心、交流合作、站内地图……(公共底部)" << endl;
	}
	void left()
	{
		cout << "Java、Python、C++、……(公共分类列表)" << endl;
	}
	void content()
	{
		cout << "C++ 学科视频" << endl;
	}
};
void test01()
{
	cout << "Java 下载视频页面如下" << endl;
	Java ja;
	ja.header();
	ja.footer();
	ja.content();
	ja.left();
	cout << "-----------------------" << endl;
	cout << "Python 下载视频页面如下" << endl;
	Python py;
	py.header();
	py.footer();
	py.content();
	py.left();
	cout << "-----------------------" << endl;
	cout << "CPP 下载视频页面如下" << endl;
	CPP cpp;
	cpp.header();
	cpp.footer();
	cpp.content();
	cpp.left();
}

int main()
{
	test01();
	system("pause");
	return 0;
}

继承实现
优点:减少重复代码
语法:class 子类 : 继承方式 父类
子类 也称为 派生类
父类 也称为 基类

派生类中的成员包含两大部分:
一类是从基类继承过来的,一类是自己增加的成员
从基类继承过来的表现其共性,而新增的成员体现了其个性

#include <iostream>
#include <string>
using namespace std;
//继承实现页面
// 公共页面
class BasePage
{
public:
	void header()
	{
		cout << "首页、公开课、登陆、注册……(公共头部)" << endl;
	}
	void footer()
	{
		cout << "帮助中心、交流合作、站内地图……(公共底部)" << endl;
	}
	void left()
	{
		cout << "Java、Python、C++、……(公共分类列表)" << endl;
	}
};
class Java : public BasePage
{
public:
	void content()
	{
		cout << "Java 学科视频" << endl;
	}
};
// Python 页面
class Python : public BasePage
{
public:
	void content()
	{
		cout << "Python 学科视频" << endl;
	}
};
// C++ 页面
class CPP : public BasePage
{
public:
	void content()
	{
		cout << "C++ 学科视频" << endl;
	}
};
void test01()
{
	cout << "Java 下载视频页面如下" << endl;
	Java ja;
	ja.header();
	ja.footer();
	ja.content();
	ja.left();
	cout << "-----------------------" << endl;
	cout << "Python 下载视频页面如下" << endl;
	Python py;
	py.header();
	py.footer();
	py.content();
	py.left();
	cout << "-----------------------" << endl;
	cout << "CPP 下载视频页面如下" << endl;
	CPP cpp;
	cpp.header();
	cpp.footer();
	cpp.content();
	cpp.left();
}

int main()
{
	test01();
	system("pause");
	return 0;
}
1.6.2 继承方式

继承方式一共有三种:

  • 公共继承
  • 保护继承
  • 私有继承

在这里插入图片描述

#include <iostream>
#include <string>
using namespace std;
//继承方式
//公共继承
class Base1
{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};
class Son1 :public Base1
{
public:
	void func()
	{
		m_A = 10;	//父类中的公共权限成员到子类中依然是公共权限
		m_B = 10;	//父类中的保护权限成员到子类中依然是保护权限
		//m_C = 10;	//父类中的私有权限成员到子类中无法访问
	}
};
//保护继承
class Son2 :protected Base1
{
public:
	void func()
	{
		m_A = 100;
		m_B = 110;
		//m_C = 120;
	}
};
//私有继承
class Son3 :private Base1
{
public:
	void func()
	{
		m_A = 100;
		m_B = 110;
		//m_C = 120;
	}
};
class GrandSon3 :public Son3
{
public:
	void func()
	{
		//m_A = 100;	//私有权限,儿子无法访问
	}
};
void test01()
{
	Son1 s1;
	s1.m_A = 100;
	//s1.m_B = 100;	//到 Son1 中 m_B 是保护权限,类外访问不到
}
void test02()
{
	Son2 s1;
	//s1.m_A = 100;	//到 Son1 中 m_A 是保护权限,类外访问不到
	//s1.m_B = 100;	//到 Son1 中 m_B 是保护权限,类外访问不到
}
void test03()
{
	Son3 s1;
	//s1.m_A = 100;	//到 Son1 中 m_A 是私有权限,类外访问不到
	//s1.m_B = 100;	//到 Son1 中 m_B 是私有权限,类外访问不到
}
int main()
{
	test01();
	system("pause");
	return 0;
}
1.6.3 继承中的对象模型

问题: 从父类继承过来的成员,哪些属于子类对象?

#include <iostream>
#include <string>
using namespace std;
//继承中的对象模型
class Base
{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};
class Son :public Base
{
public:
	int m_D;
};
void test01()
{
	//父类中所有非静态成员属性都会被子类继承下去	16
	//父类中私有成员属性是被编译器隐藏了,因此是访问不到,但是确实被继承下去了
	cout << "size of Son = " << sizeof(Son) << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

父类中所有非静态成员属性都会被子类继承下去
父类中私有成员经过公有继承后,在子类中存在,但不能直接访问
父类中私有成员属性是被编译器隐藏了,因此是访问不到,但是确实被继承下去了

—— 如何访问?
—— 可以通过子类从父类继承而来的父类里已实现的成员函数来间接访问

1.6.4 继承中构造和析构顺序

子类继承父类后,当创建子类对象,也会调用父类的构造函数
问题:父类和子类的构造和析构顺序是谁先谁后?

#include <iostream>
#include <string>
using namespace std;
class Base
{
public:
	Base()
	{
		cout << "Base 构造函数" << endl;
	}
	~Base()
	{
		cout << "Base 析构函数" << endl;
	}
};
class Son :public Base
{
public:
	Son()
	{
		cout << "Son 构造函数" << endl;
	}
	~Son()
	{
		cout << "Son 析构函数" << endl;
	}

};
void test01()
{
	//Base b;
	//继承中的构造和析构顺序如下
	//先构造父类,再构造子类,析构顺序与构造顺序相反
	Son s;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

总结:继承中,先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反

1.6.5 继承同名成员处理方式

问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢?

  • 访问子类同名成员 直接访问即可
  • 访问父类同名成员 需要加作用域
#include <iostream>
#include <string>
using namespace std;
class Base
{
public:
	Base()
	{
		m_A = 100;
	}
	void func()
	{
		cout << "Base - func() 函数调用" << endl;
	}
	int m_A;
};
class Son :public Base
{
public:
	Son()
	{
		m_A = 200;
	}
	void func()
	{
		cout << "Son - func() 函数调用" << endl;
	}
	int m_A;
};
void test01()
{
	Son s1;
	cout << "Son 下 m_A = " << s1.m_A << endl;
	//如果通过子类对象访问到父类中同名成员,需要加作用域
	cout << "Base 下 m_A = " << s1.Base::m_A << endl;
}
void test02()
{
	Son s2;
	s2.func();
	//如果通过子类对象访问到父类中同名成员函数,需要加作用域
	s2.Base::func();
}
int main()
{
	test02();
	system("pause");
	return 0;
}

总结:

  1. 子类对象可以直接访问到子类中同名成员
  2. 子类对象加作用域可以访问到父类同名成员
  3. 当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数
1.6.6 继承同名静态成员处理方式

问题:继承中同名的静态成员在子类对象上如何进行访问?

静态成员和非静态成员出现同名,处理方式一致

  • 访问子类同名成员 直接访问即可
  • 访问父类同名成员 需要加作用域
#include <iostream>
#include <string>
using namespace std;
class Base
{
public:
	static void func()
	{
		cout << "Base - static void func()" << endl;
	}
	static int m_A;
};
int Base::m_A = 100;

class Son :public Base
{
public:
	static void func()
	{
		cout << "Son - static void func()" << endl;
	}
	static int m_A;
};
int Son::m_A = 200;
//同名静态成员属性
void test01()
{
	//1. 通过对象访问数据
	Son s1;
	cout << "Son 下 m_A = " << s1.m_A << endl;
	//如果通过子类对象访问到父类中同名成员,需要加作用域
	cout << "Base 下 m_A = " << s1.Base::m_A << endl;
	//2. 通过类名方式访问
	cout << "Son 下 m_A = " << Son::m_A << endl;
	第一个 :: 代表通过类名方式访问	第二个 :: 代表访问父类作用域下
	cout << "Base 下 m_A = " << Son::Base::m_A << endl;
}
//同名静态成员函数
void test02()
{
	//1. 通过对象访问函数
	Son s2;
	s2.func();
	s2.Base::func();
	//2. 通过类名访问函数
	Son::func();
	Son::Base::func();
}
int main()
{
	test02();
	system("pause");
	return 0;
}
1.6.7 多继承语法

C++ 允许一个类继承多个类
语法:class 子类 : 继承方式 父类1, 继承方式 父类2…
多继承可能会引发父类中有同名成员出现,需要加作用域区分

C++ 实际开发中不建议用多继承

#include <iostream>
#include <string>
using namespace std;
class Base1
{
public:
	Base1()
	{
		m_A = 100;
	}
    int m_A;
};
class Base2
{
public:
	Base2()
	{
		m_B = 100;
	}
	int m_B;
};
class Son :public Base1, public Base2
{
public:
	Son()
	{
		m_C = 300;
		m_D = 400;
	}
	int m_C;
	int m_D;
};

void test01()
{
	Son s;
	cout << "sizeof Son = " << sizeof(s) << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

在这里插入图片描述

#include <iostream>
#include <string>
using namespace std;
class Base1
{
public:
	Base1()
	{
		m_A = 100;
	}
    int m_A;
};
class Base2
{
public:
	Base2()
	{
		m_A = 200;
	}
	int m_A;
};
class Son :public Base1, public Base2
{
public:
	Son()
	{
		m_C = 300;
		m_D = 400;
	}
	int m_C;
	int m_D;
};

void test01()
{
	Son s;
	cout << "sizeof Son = " << sizeof(s) << endl;
	cout << "Base1::m_A = " << s.Base1::m_A << endl;
	cout << "Base2::m_A = " << s.Base2::m_A << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}
1.6.8 菱形继承

菱形继承概念:

  • 两个派生类继承同一个基类
  • 又有某个类同时继承两给派生类
  • 这种继承被称为菱形继承,或者钻石继承
//动物类
class Animal
{
public:
	int m_Age;
};
//利用虚继承解决菱形继承问题
//继承之前 加上关键字 virtual 变为虚继承
// Animal 类称为虚基类
//羊类
class Sheep :virtual public Animal{};
//驼类
class Camel :virtual public Animal{};
//羊驼类
class Cnm :public Sheep, public Camel
{

};
void test01()
{
	Cnm cnm;
	cnm.Sheep::m_Age = 18;
	cnm.Camel::m_Age = 28;
	//当菱形继承,两个父类拥有相同数据,需要加以作用域区分
	cout << "cnm.Sheep::m_Age = " << cnm.Sheep::m_Age << endl;
	cout << "cnm.Camel::m_Age = " << cnm.Camel::m_Age << endl;
	//这份数据我们知道只要有一份就可以,菱形继承导致数据有两份,资源浪费
}

在这里插入图片描述
vbptr (v - virtual; b - base; ptr - pointer) —> vbtable
(虚基类指针指向虚基类表)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ 类和对象学习 —— 继承 的相关文章

  • 获取百度主页和系统调用

    获取百度主页 span class token function exec span 8 span class token operator lt span span class token operator gt span dev tcp
  • 文本编辑工具vim-及特殊用法,alias别名

    文章目录 简介打开文件 一 vim三种模式模式转换关闭文件特殊用法 xff1a 二 命令模式1 命令模式查找2 命令模式光标跳转3 命令模式翻屏操作4 字符编辑 xff1a 5 替换命令 r replace 6 删除命令 xff1a 7 复
  • 企业竞争分析的几种方法:SWOT、波特五力、PEST

    最近实验室要申报一个互联网 43 的项目 xff0c 项目中有关企业经营部分的内容着实令我们这些工科生无从下手 xff0c 在咨询了某专业相关的学妹后稍微有了点头绪 此处手动感谢学妹的协助哈哈哈 xff0c 本着学科交叉 xff0c 多学无
  • 解决E: 仓库 “http://ppa.launchpad.net/fcitx-team/nightly/ubuntu bionic Release” 没有 Release 文件。

    今天 xff0c 在更新软件时 xff0c 使用以下命令时 sudo apt get update sudo apt get upgrade 抛出错误 E 仓库 http ppa launchpad net fcitx team night
  • (仿牛客社区项目)Java开发笔记3.5:添加评论

    文章目录 添加评论1 dao层2 service层3 controller层4 view层5 功能测试 添加评论 根据上节的开发安排 xff1a 显示评论功能完成后 xff0c 开始实现添加评论功能 1 dao层 CommentMapper
  • js_事件

    一 常用的事件 onload 加载完成事件 页面加载完成之后 常用于做页面js代码初始化操作 onclick 单击事件 常用于按钮的点击相应操作 onblur 失去焦点事件 常用于输入框失去焦点后验证其输入内容是否合法 onchange 内
  • 操作系统学习

    目录 2 1 操作系统的启动 3 1 内存分层结构 3 2 地址空间与地址生成 3 3 内存分配 3 4 压缩式与交换式碎片整理 4 1 非连续内存 分段 4 2 非连续内存 分页 4 3 页表概述 4 4 多级页表 4 5 反向页表 5
  • 更改 tr 背景颜色无效问题

    更改tr背景颜色无效问题 x1f4c3 在更改tr背景颜色时 xff0c 我们肯定是想要整行颜色改变 xff0c 但有时会出现只有部分改变 或 全都不改变的情况 这时我们就需要去看一下自己是否在之前设计的 CSS 样式中已经给定了tr中的t
  • 【以例为引】gtsam简单入门(上)--理论和认识

    如有错漏 xff0c 请评论或者私信指出 xff0c 感谢 xff01 xff01 GTSAM简介 GTSAM xff08 Georgia Tech Smoothing and Mapping xff09 是基于因子图的C 43 43 库
  • 基于51单片机的门禁卡设计

    1 设计思路 RFID门禁系统主要采用了STC89C52RC单片机作为控制模块及读卡器RFID RC522作为识别模块 本设计实现了自动 准确的识别卡序列号 当有卡进入到读卡器读卡的范围内时就会读取到相应的卡序列号 xff0c 并根据得到的
  • STM8S程序烧录失败?调试?ST-Link方式新手向教程IAR

    首先我们要接线 xff0c 以上为某块STM8S的原理图 xff0c 我们要SWIM接SWIM xff0c NSET接RESET xff0c GND接GND xff0c 3 3接3 3 接线完成后就是软件部分了 软件部分首先要下载ST li
  • 机器学习算法——K-近邻算法(代码实现手写数字识别)

    0 引言 xff0c K 近邻算法是一种非常有效的分类算法 xff0c 它非常有效且易于掌握 原理 xff1a K 近邻算法通过计算不同样本之间的距离来分类物品 使用前 xff0c 我们需要有一个训练样本集 xff0c 并且样本集中每个数据
  • 为Navigation 2创建自定义behavior tree plugin

    系列文章目录 思岚激光雷达rplidar从ROS 1到ROS 2的移植 ROS 2下navigation 2 stack的构建 订阅rviz2的导航目标位置消息 goal pose 打断behavior tree的异步动作节点 xff0c
  • ubuntu20:/usr/bin/env: ‘python’: No such file or directory

    参考 xff1a https stackoverflow com questions 3655306 ubuntu usr bin env python no such file or directory 第一种可能 xff1a 如果没装p
  • 四轴无人飞行器 之 上位机

  • c/c++编程学习:空指针是什么?

    什么是空指针 xff1f 对于每一种指针类型 xff0c 都有一个特殊的值 空指针 xff0c 空指针与其他所有指针值区分开来 xff0c 保证其不会指向任何函数或者对象等有意义的数据 因此 xff0c 取地址运算符 amp 永远不会产生空
  • 基于ESP32的智能车WiFi图传模块实现

    基于 ESP32 C3 的多协议 WiFi 透传模块 xff08 可用作智能车图传 xff09 本项目为基于乐鑫公司的 ESP32 C3 芯片制作的无线透传模块 xff0c 具有多个通信协议接口 xff1a UART SPI 设计初衷是为了
  • 云服务器下载的镜像文件raw格式转vmdk

    使用软件qemu img https qemu weilnetz de w64 2021 下载之后安装 xff0c 然后进入安装的文件夹 xff0c 打开命令行工具然后执行下面命令 qemu img exe convert p f raw
  • keil5使用Arm Compiler 6编译出错

    Using Compiler 39 V6 15 39 folder 39 D Keil v5 ARM ARMCLANG Bin 39 main c 16 warning In file included from USER stm32f4x
  • 浏览器的相关知识

    今天在网上找到了一些需要大致了解的有关浏览器的相关知识分享 xff0c 原文链接在下方 1 浏览器的主要组成部分是什么 xff1f 用户界面 包括地址栏 前进 后退按钮 书签菜单等 除了浏览器主窗口显示的您请求的页面外 xff0c 其他显示

随机推荐

  • MySQL--用Navicat连接MySQL8.0报错1251问题解决

    文章目录 一 安装后直接用Navicat连接1251报错二 仍报错为 39 mysql 39 不是内部或外部命令 1 环境变量配置 三 找不到MySQL Server 8 0 bin路径四 解决上述全部问题 一 安装后直接用Navicat连
  • 10 分钟让你明白 MySQL 是如何利用索引的

    一 前言 在MySQL中进行SQL优化的时候 xff0c 经常会在一些情况下 xff0c 对 MySQL 能否利用索引有一些迷惑 譬如 MySQL 在遇到范围查询条件的时候就停止匹配了 xff0c 那么到底是哪些范围条件 xff1f MyS
  • 吊炸天的 Docker 图形化工具 —— Portainer

    一 Docker图形化工具二 DockerUI三 船坞四 搬运工1 查看portainer平均值2 选择喜欢的portainer风格整合 xff0c 下载3 启动dockerui容器4 xff0c 网页管理 一 Docker图形化工具 Do
  • 为提高面试通过率,技术岗可以提前做好哪些面试准备?

    Hi xff0c 大家好 xff0c 我是小庄 目前2023届秋招提前批已经陆续开始了 xff0c 考虑到一些校招的同学可能是第一次接触面试 xff08 该文章适用于校招 社招 xff09 xff0c 所以这篇文章就是为了记录一些面试技巧
  • GNU Radio自定义模块:Embedded Python Block的使用

    GNU Radio 学习使用 OOT 系列教程 xff1a GNU Radio3 8创建OOT的详细过程 基础 C 43 43 GNU Radio3 8创建OOT的详细过程 进阶 C 43 43 GNU Radio3 8创建OOT的详细过程
  • 中文分词

    本文首先介绍下中文分词的基本原理 xff0c 然后介绍下国内比较流行的中文分词工具 xff0c 如jieba SnowNLP THULAC NLPIR xff0c 上述分词工具都已经在github上开源 xff0c 后续也会附上github
  • (1)GNSS驱动nmea_navsat_driver 功能包的使用

    总览 该软件包为输出兼容NMEA语句的GPS设备提供了ROS接口 有关原始格式的详细信息 xff0c 请参见NMEA句子的GPSD文档 在成千上万的NMEA兼容GPS设备中 xff0c 我们正在汇编已知支持的设备列表 这个包是与兼容geog
  • (2)ROS传感器之GPS实践

    一 GPS接口类型 GPS接口大体可以分为两类 xff0c 一是单独的GPS接收器 xff0c 通常为USB接口 xff1b 二是与其他传感器集成 xff0c 例如激光雷达或者imu xff0c 大多是USB或者网络接口 xff0c 本文主
  • (6)GPS坐标与UTM坐标的转换

    1 简介 1 1 消息 gps common定义了两个通用消息 xff0c 供GPS驱动程序输出 xff1a gps common GPSFix和gps common GPSStatus 在大多数情况下 xff0c 这些消息应同时发布 xf
  • scanf("%c",&m)中%c前面加空格的作用

    c前面加空格不是必须的 xff0c 但有了空格就可以忽略你输入的空格 例如 xff1a scanf 34 c 34 amp m xff0c 你输入了 a a前面有个空格 xff0c a就能被c接受 但控制符前如果没空格 xff0c 那c就接
  • 聊一聊cropper.js

    最近的项目中有一个纯前端实现的功能困扰了我好久 xff0c 就是用户上传图片以后需要用户进入图片裁剪页并完成上传的功能 xff0c 一开始我是打算自己去用canvas去写这样一个页面的 xff0c 但是项目开发周期短 xff0c 任务紧 x
  • CAS服务(5.3)使用mysql验证

    CAS服务使用mysql验证 一 添加相关依赖 在pom文件里添加下面的依赖 这里cas的版本是5 3 14 lt dependency gt lt groupId gt org apereo cas lt groupId gt lt ar
  • Realsense L515 例程详解 Tutorial 1

    最近在用Realsense L515做一个机器人的视觉部分 看到网上相关资料较少 xff0c 和大家分享一下最近一周所学 第一个例程比较简单 xff0c 实现的功能也比较朴实 实现了什么功能呢 xff1f 就是把从相机得到的深度信息通过控制
  • #AI边缘计算单元-想搞开发,买树莓派还是Nano?

    作者 xff1a Blue Hole 个人网站 xff1a https www wcfde xyz xff0c 欢迎交流 近几年边缘计算快速发展 xff0c 已经渗透到各个行业 边缘计算单元也像雨后春笋涌现出来 xff0c 面对如此多的开发
  • 算法要怎么学习

    学习算法 xff0c 切记不要一上来就开始啃 算法导论 xff0c 毕竟这本书并不适合新手学习 xff0c 如果你之前的算法基础比较薄弱 xff0c 只会一直陷在 拿起来又放下 的循环里 可以怎么入门呢 xff1f 建议还是看书 43 实战
  • EGO-Swarm代码解读-地图部分

    文章目录 1 参数解读2 主要函数解读 1 参数解读 一 MappingData md 中的参数含义 xff1a local bound min span class token punctuation span local bound m
  • GNURadio中的PMTs(Polymorphic Types)数据类型

    目录 1 整体概述 2 使用方法的举例说明 3 对于PMT类型的补充说明 1 整体概述 PMTs在GNURadio中代表多态类型 xff08 Polymorphic Types xff09 xff0c 这种类型不像float int一样是严
  • STM32F103C8T6初学笔记

    STM32F103C8T6初学笔记 ST官网链接 xff1a http www stmicroelectronics com cn ST MCU网站链接 xff1a http www stmcu com cn 初识STM32 STM32是3
  • STM32F103 72MHz时钟设置

    将系统时钟初始化到72MHz的函数 根据数据手册和库函数 xff0c 设置STM32时钟为72MHz 这是 c文件 span class token macro property span class token directive key
  • C++ 类和对象学习 —— 继承

    1 6 继承 利用继承技术 xff0c 可以减少重复代码 1 6 1 继承的基本语法 普通实现 span class token macro property span class token directive keyword inclu