51创建并使用库

如何在vs中设置多个项目,以及如何创建一个能在所有项目中使用的库

51创建并使用库#

先以Game51命名创建一个项目

文件夹查看

再新建一个项目,项目名为Engine

此时


现在确保设置Game51为应用程序

Engine项目配置为静态库

创建文件夹及文件#

show All Files

目录结构

(静态链接库)Engine项目#

//src/Engine.h
#pragma once

namespace engine {
	void PrintMessage();
}
 
//不能这么写,说明:: 被称为作用域解析运算符。它的逻辑
// 是:你只能引用一个已经存在的名字空间。而这里正在定义
//所以不存在
//void engine::PrintMessage();
///src/Engine.cpp
#include "Engine.h"
#include <iostream>

namespace engine {
	void PrintMessage()
	{
		std::cout << "Hello World!" << std::endl;
	}
}

//或者这么写
//void engine::PrintMessage()
//{
//	std::cout << "Hello World!" << std::endl;
//}

(使用静态链接库)Game51项目中#

处理头文件的两个方法#

//Application.cpp

//添加头文件-方法1
//#include "../../Engine/src/Engine.h"

//直接声明-方法0
//namespace engine {
//	void PrintMessage();
//}


int main()
{

	engine::PrintMessage();
}

处理头文件的第3个方法#

右键项目Game51属性修改

50使用动态库

动态链接是什么,以及如何使用

  • 动态链接是在运行时发生的链接,当你实际启动可执行文件时,动态链接库加载时 它并非可执行文件的一部分,此时动态链接库被载入内存。意味着运行时会动态链接另一个库,然后运行应用程序 载入一个额外文件到内存。
    • 这种情况下,可执行文件实际上需要某些库存在(外部文件)。比如windows系统经常出现“缺少某些dll文件无法启动”
    • 可执行文件了解动态链接库,可执行文件是一个独立文件,在运行时加载的独立模块。但也可以加载动态链接库,他会在运行时查找并加载动态库,然后可以获取函数指针/动态库中任何内容
  • 静态链接是在编译时发生的链接,当你编译一个静态库时,将它链接到可执行文件、应用程序或动态库中。即:直接获取静态库的内容,然后把它和其他二进制数据放到一起,静态库实际存在于可执行文件或动态库中,所以编译器、链接器完全清楚这些代码,并会对此进行优化

当我们说“链接到一个动态库”时,实际上是指在创建(生成)那个 DLL 的时候,把静态库的代码塞进去。

补充:动态链接包括“隐式链接” 需要 header (.h) 和 import library (.lib) 和“显示链接” 编译阶段完全不认识这个 DLL,也不需要 .lib 导入库。用法: 运行时使用 Windows API 函数(如 LoadLibrary 和 GetProcAddress)去手动加载。

50使用动态库#

复制Dependencies文件夹到根目录

λ tree Dependencies /f

CHERNOCPP\HELLOWORLD49\DEPENDENCIES
└─GLFW
    ├─include
      └─GLFW
              glfw3.h
              glfw3native.h
    
    └─lib-vc2022
            glfw3.dll
            glfw3.lib
            glfw3dll.lib
            glfw3_mt.lib

静态链接和动态链接都是用同一部分的头文件

目前按ctrl+f7已经能正常编译

glw3dll.lib glfw3dll.lib不存代码,只有地址 ,这是指向glw3.dll的一系列指针,所以我们在运行时无需实际检测所有内容的位置,这两者同时编译很重要,因为在运行时如果尝试使用不同静态库与DLL链接,你可能会遇到函数不匹配,并且函数指针会出现内存地址错误。但是这里是由glfw分发的,所以glw3.dll和glw3dll.lib是同时编译的,而且他们直接相互关联,这两者不能分开。

接下来设置库文件

右键项目并build成功。但是目前如果会提示找不到dll文件

49使用静态库

49使用静态库#

  • 从github库或其他敌法check out代码块,在那个代码库里能直接得到编译所需的一切,并运行应用程序或项目等,无需使用包管理器
  • 倾向于实际保留依赖项的版本,实际解决方案中二进制的版本。实际项目文件夹中有物理二进制文件的副本,或者是源码

自己编译?或者直接链接预构建的二进制文件

  • 建议直接添加另一个项目(依赖项的源代码),然后编译成静态库或动态库
  • 非大型项目,可以直接链接二进制文件(无需源代码)

本篇要处理的是GLFW的库

选择二进制版本 (32位 vs 64位),这取决于你的目标应用程序架构,而不是你的操作系统。

比如现在我要制作一个32位应用程序

//结构

//里面通常是 HTML 格式的 API 手册。
├─docs
  └─html
      └─search
├─include
  └─GLFW

//含义: 针对 MinGW 编译器的版本(通常配合 GCC 使用)。
//用途: 如果你不在 Windows 上用 Visual Studio,而是用 CLion、Code::Blocks 或者直接在命令行用 g++ 编译,你需要链接这个文件夹里的库(通常是 .a 文件而非 .lib)。
├─lib-mingw-w64 
  └─libglfw3.a //(静态库): 如果你用 MinGW 编译器并想进行静态链接
  └─glfw3.dll //(动态链接库): 这是库的实体。 如果你选择动态链接,这个文件必须在运行时放在你的 .exe 旁边
  └─libglfw3dll.a //给编译器看的“目录”。 当你选择动态链接时,你不是直接链接 .dll,而是链接这个 .a 文件。 它告诉编译器:“真正的代码在 glfw3.dll 里,你先编译通过,运行时再去调它。”

//如果你的项目配置为使用通用的 Windows 10/11 SDK 运行时,有时需要专门链接这个版本的库。
├─lib-static-ucrt

//以下是库文件 (.lib),是给 链接器 用的。之所以有这么多,是因为 C++ 的二进制兼容性比较复杂,库的版本必须尽量与你使用的 Visual Studio 版本匹配
├─lib-vc2013
├─lib-vc2015
├─lib-vc2017
├─lib-vc2019
└─lib-vc2022

库由包含目录includes库目录libraries组成

47优化cpp中stdvector的使用

预习

#include <iostream>
#include <string>
#include <vector>

struct Test
{

};
struct Vertex
{
	float x, y, z;

	//有了其他的构造函数,如果需要用到无参构造函数,
	//就必须手动写一个
	Vertex()
	{

	}

	//因为有构造函数了,所以Vertex 结构体不是一个聚合类型(
	// 有构造函数),所以要使用显示构造函数
	// 使 Vertex v = { 1,2,3 }; 编译通过
	Vertex(float x, float y, float z)
	{
		this->x = x;
		this->y = y;
		this->z = z;
	}

	//参数必须是&,原因如下:
	//1. 当你尝试通过 Vertex a = b; 调用复制构造函数时,你需要将 b 传递给参数 other。
	//2. 在 C++ 中,按值传递(Pass by Value)参数本身就会触发一次复制。
	//3. 为了复制 b 到 other,编译器又需要调用复制构造函数。[死循环了]
	//因为main中使用 vector.push({1,2,3}) 需要将临时对象拷贝到vector中,所以这里的参数
	//必须是const
	Vertex(const Vertex& vertex)
	{
		std::cout << "copied constructor handled" << std::endl;
		this->x = vertex.x;
		this->y = vertex.y;
		this->z = vertex.z;
	}
	/*virtual*/ void test()
	{

	}

	~Vertex()
	{
		std::cout << "destructor handled" << std::endl;
	}
};

std::ostream& operator<<(std::ostream& stream, const Vertex& vertex)
{
	stream << vertex.x << "," << vertex.y << "," << vertex.z;
	return stream;
}

//使用引用避免复制整个数组
void Function(const std::vector<Vertex>& vertices)
{

}

int main()
{
	std::vector<Vertex> vertices;
	//vertices.push_back({ 1,2,3 });
	//std::cout << "==0===" << std::endl;
	//vertices.push_back({ 4,5,6 });

	//对上面两行代码优化
	// 1. 预先分配内存,防止搬家
	vertices.reserve(1);
	// 2. 原地构造,防止临时拷贝
	vertices.emplace_back(1, 2, 3);
	std::cout << "==0===" << std::endl;
	//超出容量,会拷贝原来的{1,2,3}到新数组,并且销毁
	// 原数组中的{1,2,3}
	vertices.emplace_back(4, 5, 6);


	std::cout << "==1===" << std::endl;
	Function(vertices);
	std::cout << "==2===" << std::endl;
	vertices.erase(vertices.begin() + 1);
	std::cout << "==3===" << std::endl;
	for (Vertex& v : vertices)
		std::cout << v << std::endl;
	std::cin.get();
}
/*
copied constructor handled //第一个临时对象复制到vector中之后,马上又销毁了
destructor handled
==0===
copied constructor handled //空间不够了,需要把第一个元素复制到新数组中。第二个元素创建临时对象,并复制到新数组中
copied constructor handled
destructor handled  //旧的数组中的第一个元素被销毁了
destructor handled  //第二个元素的临时对象被销毁了
==1===
==2===
destructor handled
==3===
1,2,3
*/

/*优化后不需要把临时对象复制到vector中,也不需要销毁临时对象了
==1===
==2===
destructor handled
==3===
1,2,3
*/

了解环境是优化过程中最重要的事情之一

46动态数组

  • 动态数组,特别是讨论标准向量类
  • 要习惯CPP的标准库(标准模版库)
  • 标准模版库包含了各种容器、迭代器、算法、仿函数

  • 容器的数据类型由程序员自己决定
  • cpp提供了一个Vector的类,在std命名空间中,可以调整大小,也可以不指定初始大小
  • 本系列会重写C++中的数据结构,优化后比标准模版库中的快很多

std::vector 的实际工作方式:

当超出实际的分配大小时。当创建一个向量,可能会先分配10个元素大小的空间,当超出这个大小时,会在内存中创建一个比原来更大的数组,并把所有内容复制过去,然后删除旧数组。这样就有了一个更大存储空间的新数组

46动态数组#

struct Test
{

};
struct Vertex 
{  
	float x, y, z;
	/*virtual*/ void test()
	{

	}
};

//main()中
Vertex v = { 1,2,3 };

聚合初始化(Aggregate Initialization),Vertex 结构体是一个聚合类型(Aggregate Type)。在 C++ 中,如果一个类或结构体满足以下条件:

  • 没有显式定义的构造函数。
  • 成员变量是公有的(public)。
  • 没有基类(没有继承)。
  • 没有虚函数。

那么你可以使用大括号 {} 直接按顺序为成员变量赋值。

vector的基本使用#

#include <iostream>
#include <string>
#include <vector>

struct Test
{

};
struct Vertex
{
	float x, y, z;

	//有了其他的构造函数,如果需要用到无参构造函数,
	//就必须手动写一个
	Vertex()
	{

	}

	//因为有构造函数了,所以Vertex 结构体不是一个聚合类型(
	// 有构造函数),所以要使用显示构造函数
	// 使 Vertex v = { 1,2,3 }; 编译通过
	Vertex(float x, float y, float z)
	{
		this->x = x;
		this->y = y;
		this->z = z;
	}

	//参数必须是&,原因如下:
	//1. 当你尝试通过 Vertex a = b; 调用复制构造函数时,你需要将 b 传递给参数 other。
	//2. 在 C++ 中,按值传递(Pass by Value)参数本身就会触发一次复制。
	//3. 为了复制 b 到 other,编译器又需要调用复制构造函数。[死循环了]
	//因为main中使用 vector.push({1,2,3}) 需要将临时对象拷贝到vector中,所以这里的参数
	//必须是const
	Vertex(const Vertex& vertex)
	{
		std::cout << "copied constructor handled" << std::endl;
		this->x = vertex.x;
		this->y = vertex.y;
		this->z = vertex.z;
	}
	/*virtual*/ void test()
	{

	}

	~Vertex()
	{
		std::cout << "destructor handled" << std::endl;
	}
};

std::ostream& operator<<(std::ostream& stream, const Vertex& vertex)
{
	stream << vertex.x << "," << vertex.y << "," << vertex.z;
	return stream;
}

int main()
{
	Vertex vertices1[5];
	Vertex* vertices2 = new Vertex[5];

	std::vector<int> myarray;
	//存储Vertex对象,则动态数组的内存是连续的,
	//其中的Vertext对象会按行排列
	//问题是:实际要调整向量大小时得复制所有(类)数据,但内存连续性
	// 带来的读取速度提升通常远超扩容时的开销
	std::vector<Vertex> vertices;
	//存储Vertext指针,实际要调整向量大小是只是复制指针地址(一个数字)
	//,即实际数据的内存地址
	std::vector<Vertex*> vertices4;

	std::cout << "---1---" << std::endl;
	Vertex v = { 1,2,3 };
	//这里演示vertices的使用
	std::cout << "---2---" << std::endl;
	vertices.push_back({ 1,2,3 });
	std::cout << "---3---" << std::endl;
	vertices.push_back({ 4,5,6 });

	std::cout << "---4---" << std::endl;
	for (int i = 0; i < vertices.size(); i++)
	{
		//c++对[]进行了重载
		std::cout << vertices[i] << std::endl;
	}
	std::cout << "---5---" << std::endl;
	//这里会把每个Vertex复制到v
	for (Vertex v : vertices)
		std::cout << v << std::endl;
	std::cout << "---6---" << std::endl;
	//避免复制
	for (Vertex& v : vertices)
		std::cout << v << std::endl;

	//clear() 会移除容器中的所有元素,但通常不一定会立即释放底层内存(容量不变),它只是销毁现有的对象。
	vertices.clear();

	std::cin.get();
}

解释一下vertices.push_back({1, 2, 3});#

当你执行 vertices.push_back({1, 2, 3}); 时,逻辑确实是:在 vector 内部的内存中,通过调用复制构造函数来构造一个属于 vector 自己的对象。

999_1柏杨跋

柏杨跋#

《资治通鉴》,司马光本是写给帝王看的,希望加强他们的统治的技术,可是事实上帝王并没有从中获益,获益的反而倒是平民,当我们发现政治舞台上的节目和《资治通鉴》上的人物举动重叠的时候,我们就可以预测它下一步的剧情。

然而,教训不是历史的主要功能,中国的实用主义文化,总是使艺术和学术作品、跟功利结合。事实上,历史的主要功能在于使我们认同我们所来自的世界,而有一种知道身世后的归属感。没有归属感的心灵,不能延续文化的薪传,而成为太空中的浮飘物。

《资治通鉴》记载太多的悲惨和丑恶,但《圣经》是犹太人的《资治通鉴》,里面有同样多的悲惨和丑恶,美国历史上有水牢、吊人树,欧洲历史上有伦敦塔、巴士底监狱,都不影响他们的后裔以他们的国家和民族为荣,中国人亦然,《资治通鉴》使我们归属于我们自己的文化,使我们对未来的发展,产生一种神圣使命,要她再没有污点、只有荣耀,使明天更有意义。

000_3导读

《资治通鉴》从公元前四〇三年写起,因为这一年发生了巨变:晋国的三家大臣——韩虔 Qián 、赵籍、魏斯瓜分了晋国,周王朝也不得不予以承认。韩、赵、魏三国的诞生,标志着一个新的时代——战国时代开始。

《战国时代》叙述了从“三家分晋”开始的一百余年的巨变。这是一个重新洗牌的时代,各国靠实力来说话。在一些国家发生的变革特别引人注目:吴起在楚国厉行政治革新,秦国采用公孙鞅的主张大兴变法,赵国厉行胡服骑射。变革的创议者如吴起、公孙鞅虽然因触犯贵族们的利益而惨死,却使这些国家因变革而国富兵强,在各国纷纷扰扰的讨伐和吞并中占了上风。齐、楚、燕、韩、赵、魏、秦,七雄并立,构成了这一时代的政治版图。

编者(柏杨)

000_2司马光进呈《资治通鉴》表

臣光言:先奉敕編集歷代君臣事迹,又奉聖旨賜名《資治通鑑》,今已了畢者。

臣司马光言:

先前,接奉圣旨,要我编纂历代君臣事迹。不久,再接奉圣旨,赐名《资治通鉴》。现在,全书已完全定稿。

伏念臣性識愚魯,學術荒疏,凡百事爲,皆出人下。獨於前史,粗嘗盡心,自幼至老,嗜之不厭。每患遷、固以來,文字繁多,自布衣之士,讀之不徧,況於人主,日有萬機,何暇周覽!臣常不自揆,欲删削宂長,舉撮機要,專取關國家盛衰,繫生民休戚,善可爲法,惡可爲戒者,爲編年一書,使先後有倫,精粗不雜,私家力薄,無由可成。

我性情愚昧而且鲁莽,学术更是荒疏,所做的事,都在别人之下。唯独对于历史,心有所爱,从幼到老,嗜好不倦。深深地感觉到,自从司马迁、班固以来,史籍越来越多,普通人有的是时间,还读不完,更何况高高在上的君王,日理万机,哪有闲暇?我常怀一种抱负,打算加以整理,删除多余的废话,摘取其中的精华,专门收集有关国家兴衰,人民悲欢,善可以为法,恶可以为戒的政治行为,编著一部编年史。使先后顺序,明确呈现,内容篇幅,繁简适当。只因为私人力量单薄,无法着手。

伏遇英宗皇帝,資睿智之性,敷文明之治,思歷覽古事,用恢張大猷,爰詔下臣,俾之編集。臣夙昔所願,一朝獲伸,踊躍奉承,惟懼不稱。先帝仍命自選辟官屬,於崇文院置局,許借龍圖、天章閣、三館、祕閣書籍,賜以御府筆墨繒帛及御前錢以供果餌,以內臣爲承受,眷遇之榮,近臣莫及。不幸書未進御,先帝違棄羣臣。陛下紹膺大統,欽承先志,寵以冠序,錫之嘉名,每開經筵,常令進讀。臣雖頑愚,荷兩朝知待如此其厚,隕身喪元,未足報塞,苟智力所及,豈敢有遺!會差知永興軍,以衰疾不任治劇,乞就宂官。陛下俯從所欲,曲賜容養,差判西京留司御史臺及提舉嵩山崇福宮,前後六任,仍聽以書局自隨,給之祿秩,不責職業。臣旣無他事,得以研精極慮,窮竭所有,日力不足,繼之以夜。徧閱舊史,旁采小說,簡牘盈積,浩如煙海,抉擿幽隱,校計豪釐。上起戰國,下終五代,凡一千三百六十二年,修成二百九十四卷。又略舉事目,年經國緯,以備檢尋,爲《目錄》三十卷。又參考羣書,評其同異,俾歸一塗,爲《考異》三十卷。合三百五十四卷。自治平開局,迨今始成,歲月淹久,其間抵牾,不敢自保,罪負之重,固無所逃。臣光誠惶誠懼,頓首頓首。


称呼类型具体名称备注
庙号宋英宗文中提到的“英宗皇帝”。
姓名赵曙文中开头提到的名字。
曾用名赵宗实文中括号里提到的名字(他过继给仁宗前叫这个)。
排序北宋第五位皇帝承接宋仁宗,开启了宋神宗时代。

幸而遇到英宗皇帝(宋王朝五任帝赵曙),聪明睿智,关心文化推展,想了解古时政事,借此作为制定国家大计方针的根据。特地下令,教我着手编纂。往日的愿望,忽然可以发挥,欢欣鼓舞,不能自已。唯一恐惧的是,才疏学浅,难以胜任。先帝(五任帝赵宗实)又命我自己物色任用助手,在崇文院内,设立编辑局,准许向龙图阁、天章阁、“三馆”(昭文馆、集贤馆、国史馆)以及秘阁等图书馆,借用图书。并发给御用的笔墨纸砚,更特别犒赏,购买水果点心。并指定宦官充当联络官,直接可以奏报先帝。受恩之深,受宠之隆,近代从来没有。不幸书还没有进呈,先帝竟行去世。陛下(六任帝赵顼)继位大统,也继承遗志,颁赐序文,亲为本书命名。御前讲座时,也常命我宣读。我虽然愚昧,但受到两任皇上如此厚待,即令杀身枭首,也不能报答万一。只要能力够用,岂敢有丝毫惰怠?那时,政府派我代理永兴(陕西省西安市)战区司令官(知永兴军),因身体衰弱,又患病未痊,不能从事繁重工作,请求改调其他官职。陛下顾念下情,答应我的请求,命我担任西京(河南省洛阳市)留守政府监察总监(判西京留司御史台),兼任西京嵩山崇福宫管理官(提举西京嵩山崇福宫)。前后六次调动职务,都准允编辑局跟我一同迁移。并且只发经费,从不规定按时缴出成绩。我既没有其他重大事务,就投入全部精力,精细研究,竭尽心力。白天不够使用,继之以黑夜。不但选录正史,还从旁采及野史(小说)、书信和文件,堆积得好像大海。我们在最隐秘处发掘历史真相,对每一个字都校正它是否错误。上起战国,下至五代,凡一千三百六十二年,共二百九十四卷。另外,再编索引,以年月为纵的轨迹,以事件为横的叙述,便于读者查考,命名《目录》,凡三十卷。再另外,参考各种图书,考证它们的异同,说明真伪,再成《考异》三十卷。——总共三百五十四卷。回溯过去,自本世纪(十一世纪)六〇年代开始着手,到今天才算完成,悠悠岁月,中间受到政局影响,我自己都不敢相信能够平安,负罪至重,不容逃避。

重念臣違離闕庭,十有五年,雖身處于外,區區之心,朝夕寤寐,何嘗不在陛下之左右!顧以駑蹇,無施而可,是以專事鉛槧,用酬大恩,庶竭涓塵,少裨海嶽。臣今骸骨癯瘁,目視昏近,齒牙無幾,神識衰耗,目前所爲,旋踵遺忘。臣之精力,盡於此書。伏望陛下寬其妄作之誅,察其願忠之意,以清閒之宴,時賜有覽,監前世之興衰,考當今之得失,嘉善矜惡,取得捨非,足以懋稽古之盛德,躋無前之至治。俾四海羣生,咸蒙其福,則臣雖委骨九泉,志願永畢矣!

臣司马光,诚惶诚恐,顿首顿首。敬请陛下垂念我远离中央,已十有五年,虽然身在外地,但区区之心,无论早上或黄昏,无论清醒或睡梦,何尝不在陛下左右。只以天性拙笨,无从效力,是以专门从事文字工作,报答皇恩,只求点滴之水和微粒之尘,以增加大海之深和大山之高。我现在骨骸憔悴,双目近视,牙齿几乎全部脱落,精神耗损枯竭。眼前办的事,一转身就都忘掉,残余精力,在此书上全部耗尽。敬请陛下(六任帝赵顼)宽恕我因妄自著作而应诛杀的重罪,俯察我一念之忠,在清闲休息的时候,顺手翻阅。==参考前代王朝的兴衰,考查当今政治措施的得失,嘉奖善良,排除罪恶,坚持正义,改正错误,就足可以追踪古代的盛世,使国家迈入以前从没有过的太平境界。==四海之内的苍生,都蒙受到福祉。那么,我虽葬身黄泉之下,平生志愿也已得到回报。

謹奉表陳進以聞。臣光誠惶誠懼,頓首頓首,謹言。

端明殿學士兼翰林侍讀學士太中大夫提舉西京嵩山崇福

宮上柱國河內郡開國公食邑二千六百戶食實封一千戶臣

司馬光 上表

元豐七年十一月進呈

端明殿学士兼翰林侍读学士、太中大夫、提举西京嵩山崇福宫、上柱国、河内郡开国公、食邑二千六百户、实封一千户臣司马光上表。

一〇八四年十一月进呈

檢閱文字承事郞 司馬康

同修奉議郞 范祖禹

同修祕書丞 劉 恕

同修尚書屯田員外郞充集賢校理 劉 攽

編集端明殿學士兼翰林侍讀學士太中大夫 司馬光


獎諭詔書 敕司馬光:修《資治通鑑》成事。

史學之廢久矣,紀次無法,論議不明,豈足以懲勸,明久遠哉!卿博學多聞,貫穿今古,上但晚周,下迄五代,發揮綴緝,成一家之書,褒貶去取,有所據依。省閱以還,良深嘉歎!今賜卿銀絹、對衣、腰帶、鞍轡馬,具如別錄,至可領也。故茲獎諭。想宜知悉。

冬寒,卿比平安好。遣書,指不多及。  十五日。

元豐八年九月十七日准尚書省劄子奉聖旨重行校定

元祐元年十月十四日奉聖旨下杭州鏤板

000_1序

柏杨序#

资治通鉴包含了中国历史上最混乱和苦难的四个时代

  • 战国时代   前四八〇—前二二一年
  • 三国时代   二二〇—二八〇年
  • 大分裂时代  三〇四—五八九年
  • 小分裂时代  九〇七—九五九年

赵顼,北宋第六任皇帝,将它命名为《资治通鉴》

翻译上最大的困难约有三点:一是地名,中国人是世界上最勇于更改地名的民族,古地何在?好像都在云端。二是官名,历代官职名称,奇异怪诞,往往匪夷所思。三是时间,“年”不写“年”,而写“著雍摄提格”,“日”不写“日”,而写“甲子乙丑”。我们的方案是:地名仍用古地名,夹注今地名,而另行绘制地图,越详细越好,使历史人物,生活在实际舞台之上。官名则全用现代人所了解的称谓,夹注原称,盖必须如此,才能确知它的权力关系。至于==“年”,我们使用公元==。只有公元才可显现时间距离,不但不再沾惹“著雍摄提格”,连年号也作为配件,摆脱争执最烈的“正朔”困扰。至于“日”,我们使用数字,摆脱“甲子乙丑”。

翻译工作直到今年(一九八三年),才获实现。

文言文最大的特征是,没有主词,往往前言不照后语,前言在东,后语忽然在西,难以连贯。典故堆砌,意义更容易混淆。以及地名今注,官名今译,全都费尽思考。

一九八三年七月十五日于台北

柏杨再序#

一九八三年七月,柏杨版《资治通鉴》第一册开始问世时,曾经写一篇“序”。而今,一九九三年三月,当七十二册平装本,改成三十六册精装本发行时,再提笔写这篇“再序”。

执笔之初台湾海峡还不能逾越,于写到尾声时,两岸已交流频繁。执笔之初我们所用的还是四十年前的老地图,于写到尾声时,已可公开使用大陆地名。以致,我们的后续工作,比其他巨著的后续工作,加倍复杂。

一九九三年三月七日于台北

赵顼序#

朕惟君子多識前言往行以畜其德,故能剛健篤實,輝光日新。《書》亦曰:「王,人求多聞,時惟建事。」《詩》、《書》、《春秋》,皆所以明乎得失之迹,存王道之正,垂鑑戒於後世者也。

我知道,高级知识分子差不多都熟悉前代所发生的事情,用以砥砺品德。所以他们才心理健康,神采四射,每天向前进步。《尚书》说:“君王应该不断学习,时刻不停地全神贯注。”《诗经》《尚书》《春秋》,每部书都在说明得失的轨迹,保护无偏无私的正规法则,使后世从记载中得到教训和警惕。

漢司馬遷紬石室金匱之書,據左氏《國語》,推《世本》、《戰國策》、《楚漢春秋》,采經摭傳,罔羅天下放失舊聞,考之行事,馳騁上下數千載間,首記軒轅,至于麟止,作爲紀、表、世家、書、傳,後之述者不能易此體也。惟其是非不謬於聖人,褒貶出於至當,則良史之才矣。

西汉王朝司马迁,整理皇家祖庙石室里的书籍和皇家库房金柜里的文件,再根据《左氏国语》《世本》《战国策》《楚汉春秋》,广为收集,精密选择,网罗历史上的故事佚闻,再加以考证,笔触奔腾于上下数千年之间。最早起于黄帝王朝第一任君王姬轩辕(前二六九八年),最晚至于刘彻(西汉王朝七任帝)发现麒麟(前一二二年。共二千五百七十六年)。内容分成“纪”“表”“世家”“书”“传”五个单元,遂成为定型,后世史学家跳不出他所创立的模式。司马迁主要的优点,在于是非判断,都不违背圣人的标准,赞扬和谴责,也都十分中肯。毫无疑义地,他是一位优良的史学家。

若稽古英考,留神載籍,萬機之下,未嘗廢卷。嘗命龍圖閣直學士司馬光論次歷代君臣事迹,俾就祕閣繙閱,給吏史筆札,起周威烈王,訖于五代。光之志以爲周積衰,王室微,禮樂征伐自諸侯出,平王東遷,齊、楚、秦、晉始大,桓、文更霸,猶託尊王爲辭以服天下;威烈王自陪臣命韓、趙、魏爲諸侯,周雖未滅,王制盡矣!此亦古人述作造端立意之所繇也。其所載明君、良臣,切摩治道,議論之精語,德刑之善制,天人相與之際,休咎庶證之原,威福盛衰之本,規模利害之效,良將之方略,循吏之條敎,斷之以邪正,要之於治忽,辭令淵厚之體,箴諫深切之義,良謂備焉。凡十六代,勒成二百九十六『章:乙十一行本,六作四。』卷,列于戶牖之間而盡古今之統,博而得其要,簡而周于事,是亦典刑之總會,冊牘之淵林矣。

我父亲(宋王朝五任帝赵曙)一向重视古籍,留意图册,虽然每天处理千万国家事务,但一有空暇,仍沉湎阅读。曾经委托龙图阁常设皇家文学侍从官(龙图阁直学士)司马光,研究历代君王和官员们的事迹,就近向秘阁御用图书馆,收集资料,由政府供应全部经费。起自公元前四〇三年,讫于公元后九五九年。司马光的意思是,周王朝日益陵替,皇族衰弱,法令规章和军事行动,都操在封国国君之手。十三任王(平王)姬宜臼把首都自镐京(陕西省西安市西)东迁到洛阳,齐、楚、秦、晋,诸封国才开始强大。姜小白、姬重耳,先后成为霸主,但仍尊重周王朝的国王,用以号令天下。可是,到了三十八任国王(威烈王)姬午,下令擢升==封国的高级官员(陪臣)韩、赵、魏三家,当封国国君(诸侯),周王朝虽未灭亡,纲纪却已全毁。==司马光决定从发生这一件事的那一年开始,也正是古人著书立说,从某一事件起笔的原意。至于书中引用圣君贤相们讨论国家大事和治理之道的精辟言论,道德的或刑罚的善恶制度,神明的和人世的之间的关系,吉祥的和灾难的根源,威信盛衰的基础,行政措施利与害的影响,将领们的战略,官吏们的施政方案,严格地分析它们是邪恶?还是公正?是长久之计?还是只顾眼前?不仅于此而已,连词藻美丽的文章,含理至深的议论,也都一一收集。历经十六个王朝,凡二百九十四卷。把它展开在明窗净几之上,立刻可以了然古今的演变历程。广博而扼要,简洁而不遗漏。更是一种典章制度的总汇、文章词藻的选辑。

荀卿有言:「欲觀聖人之迹,則於其粲然者矣,後王是也。」若夫漢之文、宣,唐之太宗,孔子所謂「吾無間焉」者。自餘治世盛王,有慘怛之愛,有忠利之敎,或知人善任,恭儉勤畏,亦各得聖賢之一體,孟軻所謂「吾於《武成》取二三策而已」。至于荒墜顚危,可見前車之失;亂賊姦宄,厥有履霜之漸。《詩》云:「商鑑不遠,在夏后之世。」故賜其書名曰「《資治通鑑》」,以著朕之志焉耳。

荀况 (荀子) 曾说过:“你如果想看圣人的做人行事,应该在后代的英明君王身上寻找。”像西汉王朝的刘恒(五任帝)、刘询(十任帝),唐王朝的李世民(二任帝),都是孔丘所说的,无可挑剔的人物。其他的英明君王,或有诚挚的爱心,或有忠孝的感召,或者知人善任,或者勤俭谨慎,也都得到圣贤们的部分优点。孟轲说:“我对于姬发(周王朝一任王)、姬诵(周王朝二任王),只赞扬他们两三件事而已。” 孟子对齐宣王说: 你不需要每天都当圣人,你只要能在关键决策上做出两三件真正利民的大事,你就是当代的周武王。 至于有的荒谬狂悖,我们可从他看到前车之鉴。有的恶毒奸诈,可从其中得到反省和启示。《诗经》说:“商王朝子孙,应以夏王朝的覆亡,作为借镜,不必远求。”所以,我特地为这部书取名《资治通鉴》,显示我的盼望。


(司马光注)

治平四年十月初開經筵,奉聖旨讀《資治通鑑》。其月九日,臣光初進讀,面賜御製序,令候書成日寫入。

治平四年(一〇六七年)十月初,皇上召大臣讲课,我奉到圣旨,宣读《资治通鉴》。该月九日,臣司马光第一次进读,皇上把御制的序文,当面赐下,吩咐:“等全书完成时加进去。”

著雍閹茂正月丙戌電子版讎覆計脫文一

45箭头运算符

在 C++ 中,-> 运算符有一个独特的“递归”特性。当你写下 entity->Print() 时,编译器会查看 entity 的类型:

  • 如果 entity 是原生指针:它直接解引用并访问成员。
  • 如果 entity 是一个对象(类实例):
    1. 编译器会调用你重载的 operator->()。
    2. 这个函数必须返回两样东西之一:要么是一个原生指针,要么是另一个重载了 -> 的对象。
    3. 编译器拿到返回的指针后,自动再次对它使用箭头操作,直到找到最终的成员。

箭头运算符的使用及重载#

#include <iostream>
#include <string>

class Entity
{
public:
	int x;
public:
	void Print() const
	{
		std::cout << "Hello!x=" << x << std::endl;
	}
};

//创建这个类的对象时传入一个堆上的对象,
//可以使用堆对象,且堆对象会被自动释放
class ScopePtr
{
private:
	Entity* m_Obj;
public:
	ScopePtr(Entity* entity)
		:m_Obj(entity)
	{

	}
	~ScopePtr()
	{
		std::cout << "release m_Obj" << std::endl;
		delete m_Obj;
	}

	Entity* GetObject()
	{
		return m_Obj;
	} 
};

int main()
{
	Entity e;
	e.Print();

	//取消引用后用 .函数
	Entity* ptr = &e;
	Entity& entity = *ptr;
	//entity.Print();
	//(*ptr).Print();
	ptr->x = 2;
	ptr->Print();
	{
		ScopePtr entity = new Entity();
		entity.GetObject()->Print(); 
	}

	std::cin.get();
}

重载箭头运算符#

#include <iostream>
#include <string>

class Entity
{
public:
	int x;
public:
	void Print() const
	{
		std::cout << "Hello!x=" << x << std::endl;
	}
};

//创建这个类的对象时传入一个堆上的对象,
//可以使用堆对象,且堆对象会被自动释放
class ScopePtr
{
private:
	Entity* m_Obj;
public:
	ScopePtr(Entity* entity)
		:m_Obj(entity)
	{

	}
	~ScopePtr()
	{
		std::cout << "release m_Obj" << std::endl;
		delete m_Obj;
	}

	Entity* GetObject()
	{
		return m_Obj;
	}

	//1. 告诉编译器:“我把内部的指针给你用,但你只能
	// 看,不能改指针指向的那个 Entity。”
	//2. 右边的const:“这个函数是一个‘只读’函数,它不会修改
	// ScopedPtr 对象内部的任何成员变量。”,而且,他的返回值
	//不能调用非const函数
	const Entity* operator->() const 
	{
		return m_Obj;
	}
};

int main()
{
	Entity e;
	e.Print();

	//取消引用后用 .函数
	Entity* ptr = &e;
	Entity& entity = *ptr;
	//entity.Print();
	//(*ptr).Print();
	ptr->x = 2;
	ptr->Print();
	{
		//隐式转换,编译器会寻找匹配的构造函数
		//等同于:ScopePtr entity(new Entity()); // 直接初始化
		// 或者 ScopePtr entity = ScopePtr(new Entity()); // 显式转换
		ScopePtr entity = new Entity();
		entity.GetObject()->Print();
		entity->Print();
	}

	std::cin.get();
}

对于ScopePtr entity = new Entity();,编译器的逻辑