最近在linux上跑一些开源库做学习用, 顺手就搭了一下vscode的c++开发环境, 这里分享一下vscode进行C++开发的基本环境结构.

1. 首先是编辑器, vscode直接官网下载的, 后期可以用 apt 直接更新, 个人觉得还是挺方便的, 有喜欢折腾的小伙伴可以去github上拉开源版本的下来自己编译, 这里不过多赘述

2. 其次是编译器, 我使用的是GNU编译器g++, 生成脚本我选择了makefile

以上是基础工具, 如果把vscode换成vim + shell脚本, 调试直接gdb的话, 就基本上是原生环境开发了

接下来就是开发环境的搭建了, 这里我先整理一下一个工程量稍微大一些的项目所应该包含的项目种类, 再根据整理的结果给出一个我写的例子, 之后再对该例进行不断完善

对于一个大型工程来说, 可能至少会包含以下几种不同的工程:

1. 可执行程序 : 即项目主要的目标

2. 静态库 : 集成一些基础的工具函数和一些基础功能的封装

3. 动态库 : 作为插件, 非核心功能之类的东西

4. 资源文件 : 各种图片, 文件, 音频, xml等等

以上是我认为的一个工程量稍大的程序可能会包含的项目种类, 根据上面这四类, 我构建了如下的文件结构 :

.
├── debug
├── lib
├── project
│   ├── debug.makefile
│   ├── exe_test
│   │   ├── compile
│   │   ├── .d
│   │   ├── header
│   │   │   └── test.h
│   │   ├── makefile
│   │   └── src
│   │       └── test.cpp
│   ├── lib_a
│   │   ├── compile
│   │   ├── .d
│   │   ├── header
│   │   │   ├── a_1st.h
│   │   │   ├── a_2nd.h
│   │   │   └── a_3rd.h
│   │   ├── makefile
│   │   └── src
│   │       ├── a_1st.cpp
│   │       ├── a_2nd.cpp
│   │       └── a_3rd.cpp
│   ├── lib_so
│   │   ├── compile
│   │   ├── .d
│   │   ├── header
│   │   │   ├── so_1st.h
│   │   │   ├── so_2nd.h
│   │   │   └── so_3rd.h
│   │   ├── makefile
│   │   └── src
│   │       ├── so_1st.cpp
│   │       ├── so_2nd.cpp
│   │       └── so_3rd.cpp
│   └── makefile
├── release
└── .vscode
    ├── c_cpp_properties.json
    ├── launch.json
    ├── settings.json
    └── tasks.json

20 directories, 23 files

在当前项目目录下共有4个子目录和一个vscode专用的隐藏目录 :

1. debug : 所有我们生成的debug版本的可执行程序以及debug版本程序所需的资源都会生成在这个目录中

2. release : 同上, 但可执行程序和资源文件都是release版的

3. lib : 所有动态库, 静态库会生成在这个目录中, debug版和release版用文件名结尾是否带 D 来区分

4. project : 所有当前项目相关的工程都在这个目录中

5. .vscode : vscode专用目录, 其中包含了当前项目相关的vscode配置信息

下面再看一下project目录, 该目录下共有3个项目目录和两个makefile :

1. lib_a : 该项目最终会生成一个静态库供程序使用

2. lib_so : 该项目最终会生成一个动态库供程序使用

3. exe_test : 该项目最终会生成一个可执行程序, 该程序会使用到上述静态库和动态库

4. 两个makefile用于控制所有项目的debug版, release版生成

最后再解析一下每一个项目目录, 每个项目都包含了4个子目录和一个makefile :

1. src : 所有的源文件放置在该目录中

2. header : 所有的头文件放置在该目录中

3. compile : 编译后的.o文件会在这个目录中生成

4. .d : 该目录用于存放每个源文件的依赖关系

5. makefile : 该makefile控制当前项目的生成

以上是例子文件结构的大概说明, 下面我们就这个例子进行完善, 针对每一个工程和整个项目, 编写makefile, 完成代码的编译生成

首先针对整个项目, 我们要生成每一个工程, 并保证工程的生成顺序符合每个工程间的依赖关系

这里先看一下project/makefile, 这个makefile用于生成所有工程release版本

 export BUILD_VER := RELEASE
export CXXFLAGS := -Wall -std=c++
export RM := rm -f .PHONY:build_all clean_all clean_all_cache build_all:
cd ./lib_so && make
cd ./lib_a && make
cd ./exe_test && make clean_all:
cd ./lib_so && make clean
cd ./lib_a && make clean
cd ./exe_test && make clean clean_all_cache:
cd ./lib_so && make clean_cache
cd ./lib_a && make clean_cache
cd ./exe_test && make clean_cache

该makefile首先会覆写3个变量, 并将变量导出成为全局变量, 其中BUILD_VER用于控制生成程序的版本, 紧随其后的是3个伪目标, 分别用于生成每个工程, 清理所有生成文件以及清理生成过程中的产生的.o和.d

接下来再来看project/debug.makefile, 这个makefile用于生成所有工程的debug版本

 include ./makefile

 BUILD_VER := DEBUG

该makefile引入release版的makefile, 并修改BUILD_VER为DEBUG, 该makefile名称不是make能够自动识别的名称, 使用需要加上 -f 参数, 如 : make -f debug.makefile

通过上面两个makefile, 我们基本完成了对代码生成的版本控制和整个项目的生成流程, 下面只需要针对每一个工程, 编写对应的makefile即可

下面是3个工程的makefile :

首先是静态库工程lib_a

 vpath %.cpp ./src
vpath %.h ./header .PHONY: all clean clean_cache
all : # 默认目标 CXXINCLUDES = -I ./header
ARFLAGS = -rcs
SRCS_WITH_PATH = $(wildcard ./src/*.cpp)
SRCS = $(SRCS_WITH_PATH:./src/%.cpp=%.cpp)
DEPS = $(SRCS:.cpp=.d)
DEPS_WITH_PATH = $(SRCS:%.cpp=./.d/%.d)
OBJS = $(SRCS:.cpp=.o)
OBJS_WITH_PATH = $(SRCS:%.cpp=./compile/%.o)
TARGET_NAME = tsi.a
OUTDIR = ../../lib/ ifeq ($(BUILD_VER), DEBUG)
CXXFLAGS += -g3
TARGET_NAME := tsiD.a
endif ifeq ($(BUILD_VER), RELEASE)
CXXFLAGS += -O2
endif #生成依赖关系,保证修改.h时也会重新编译相关.cpp
-include $(DEPS) %.d:$(SRCS)
@set -e;\
$(RM) $@;\
$(CXX) $(CXXINCLUDES) -MM $< > .d/$@; %.o:%.cpp
$(CXX) $(CXXFLAGS) $(CXXINCLUDES) -c $< -o ./compile/$@ all:$(TARGET_NAME) $(TARGET_NAME):$(OBJS)
$(AR) $(ARFLAGS) $(OUTDIR)$(TARGET_NAME) $(OBJS_WITH_PATH) clean:
$(RM) $(OUTDIR)$(TARGET_NAME) $(OBJS_WITH_PATH) $(DEPS_WITH_PATH) clean_cache:
$(RM) $(OBJS_WITH_PATH) $(DEPS_WITH_PATH)

makefile中首先读取了当前工程下的两个目录, 保证正确搜索.h和.cpp之后声明三个伪目标, 并以all为终极目标, 之后声明了一系列变量, 这里详细解释一下每一个变量, 跟大家解释一下我的思路

CXXINCLUDES : 该变量包含了生成时c++的包含目录

ARFLAGS : 静态库打包标志

SRCS_WITH_PATH : 包含路径的所有源文件, 该写法可以自动匹配指定目录下的所有.cpp, 大型工程中可能会有很多源文件, 每次更新删除都要修改makefile的话会很不方便

SRCS : 剔除所有源文件的前缀路径

DEPS : 对每一个源文件, 生成一个对应的写有依赖关系的.d文件

DEPS_WITH_PATH : 包含前缀路径的全部.d文件

OBJS : 源文件编译生成的全部.o文件

OBJS_WITH_PATH : 包含前缀路径的全部.o文件

TARGET_NAME : 生成目标的名称

OUTDIR : 输出目录

在声明了以上这些变量之后, 通过对全局变量BUILD_VER的值的判断, 在CXXFLAGS里添加不同的参数以控制版本, 并对文件名等信息做修改

接下来我用-include让当前makefile读取所有.d依赖关系, 当前文件由于没有找到这些.d文件, 会在文件中搜索有无生成的静态目标, 这时, make会搜索到下方的%.d:$(SRCS)

根据该静态目标, .d文件便会被生成出来并被加载

假设我们当前指明生成的是伪目标all

all所依赖的目标是我们指定的文件名$(TARGET_NAME), 该变量所指向的目标又依赖于所有的.o文件, 由于.o文件没有被生成, make又会搜索并调用静态目标%.o:%.cpp进行.o文件的生成

在生成完所有的.o文件之后, 目标$(TARGET_NAME)才会被执行, 最终在../../lib目录中生成tsi.a或tsiD.a

理解了上面的内容之后, 接下来两个工程 : 动态库以及可执行文件的makefile基本也可以套用上面的内容再进行修改得到, 这里我贴出我的写法供大家参考

动态库makefile

 vpath %.cpp ./src
vpath %.h ./header .PHONY: all clean clean_cache
all : # 默认目标 CXXFLAGS += -fPIC
CXXINCLUDES = -I ./header
SRCS_WITH_PATH = $(wildcard ./src/*.cpp)
SRCS = $(SRCS_WITH_PATH:./src/%.cpp=%.cpp)
DEPS = $(SRCS:.cpp=.d)
DEPS_WITH_PATH = $(SRCS:%.cpp=./.d/%.d)
OBJS = $(SRCS:.cpp=.o)
OBJS_WITH_PATH = $(SRCS:%.cpp=./compile/%.o)
TARGET_NAME = libtest.so
OUTDIR = ../../lib/ ifeq ($(BUILD_VER), DEBUG)
CXXFLAGS += -g3
TARGET_NAME := libtestD.so
endif ifeq ($(BUILD_VER), RELEASE)
CXXFLAGS += -O2
endif #生成依赖关系,保证修改.h时也会重新编译相关.cpp
-include $(DEPS) %.d:$(SRCS)
@set -e;\
$(RM) $@;\
$(CXX) $(CXXINCLUDES) -MM $< > .d/$@; %.o:%.cpp
$(CXX) $(CXXFLAGS) $(CXXINCLUDES) -c $< -o ./compile/$@ all:$(TARGET_NAME) $(TARGET_NAME):$(OBJS)
$(CXX) -shared -o $(OUTDIR)$(TARGET_NAME) $(OBJS_WITH_PATH) clean:
$(RM) $(OUTDIR)$(TARGET_NAME) $(OBJS_WITH_PATH) $(DEPS_WITH_PATH) clean_cache:
$(RM) $(OBJS_WITH_PATH) $(DEPS_WITH_PATH)

可执行程序makefile

 vpath %.cpp ./src
vpath %.h ./header .PHONY: all clean clean_cache
all : # 默认目标 CXXINCLUDES = -I ./header -I ../lib_a/header -I ../lib_so/header
SRCS_WITH_PATH = $(wildcard ./src/*.cpp)
SRCS = $(SRCS_WITH_PATH:./src/%.cpp=%.cpp)
DEPS = $(SRCS:.cpp=.d)
DEPS_WITH_PATH = $(SRCS:%.cpp=./.d/%.d)
OBJS = $(SRCS:.cpp=.o)
OBJS_WITH_PATH = $(SRCS:%.cpp=./compile/%.o)
LINK_LIB = ../../lib/tsi.a
LINK_USR_SO = -L ../../lib -Wl,-rpath=../lib -ltest
TARGET_NAME = test
OUTDIR = ../../release/ ifeq ($(BUILD_VER), DEBUG)
CXXFLAGS += -g3
LINK_LIB := ../../lib/tsiD.a
LINK_USR_SO := -L ../../lib -Wl,-rpath=../lib -ltestD
TARGET_NAME := testD
OUTDIR := ../../debug/
endif ifeq ($(BUILD_VER), RELEASE)
CXXFLAGS += -O2
endif #生成依赖关系,保证修改.h时也会重新编译相关.cpp
-include $(DEPS) %.d:$(SRCS)
@set -e;\
$(RM) $@;\
$(CXX) $(CXXINCLUDES) -MM $< > .d/$@; %.o:%.cpp
$(CXX) $(CXXFLAGS) $(CXXINCLUDES) -c $< -o ./compile/$@ all:$(TARGET_NAME) $(TARGET_NAME):$(OBJS)
$(CXX) -o $(OUTDIR)$(TARGET_NAME) $(OBJS_WITH_PATH) $(LINK_LIB) $(LINK_USR_SO) clean:
$(RM) $(OUTDIR)$(TARGET_NAME) $(OBJS_WITH_PATH) $(DEPS_WITH_PATH) clean_cache:
$(RM) $(OBJS_WITH_PATH) $(DEPS_WITH_PATH)

这里有几点需要注意的是, 在可执行程序链接时, 我用-Wl,-rpath指定了程序执行时回去何处寻找libtest.so这个动态库, 如果不想这样写, 需要指定动态库生成到系统默认加载的路径中去, 比如/usr/lib, 同样程序在其他机器上部署时也需要做同样的操作

另外就是关于.d依赖生成我使用的参数是-MM, 因为GNU编译器如果使用-M参数会自动加入一些其它的依赖关系, 具体内容可以用g++ -M xxx.cpp做简单验证, 如下图:

-MM:

-M(后面还有....):

在完成了上述步骤之后, 我们的项目其实已经可以正常编译生成执行了, 只是跟vscode没什么联系, 这里我们先在project目录中运行make, make clean_all, make, make clean_all_cache来看一下辛苦编写makefile的成果

很成功, 舒服了

接下来, 为了做到一键运行(F5)或者一键debug调试, 我们要对vscode进行项目配置, 这里我们要修改.vscode目录下的三个文件:launch.json task.json c_cpp_properties.json

在此之前先贴一下我在vscode中安装的插件, 这些插件能让开发环境更美观, 代码编写更顺畅

其中C/C++提供了只能高亮头文件查找等功能, Chinese是一些选项的汉化, Font Switcher可以快速更换字体(这个无所谓...), One Dark Pro是一款比较美观的主题配色, Python(个人需求, 写一些简单的脚本还是很方便的), TabOut可以针对各种括号按tab快速跳出, vscode-icons美化各种图标

下面到了vscode的启动配置, 在vscode的运行选项卡中, 我们可以选择当前项目启动的配置, 该配置集由launch.json来控制, 这里我先贴出我的launch.json, 再进行详细说明

 {
"configurations": [
{
"name": "run release",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/release/test",
"args": ["-r", "-debug"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/release",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "为 gdb 启用整齐打印",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "make release"
},
{
"name": "run debug",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/debug/testD",
"args": ["-r", "-debug"],
"stopAtEntry": true,
"cwd": "${workspaceFolder}/debug",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "为 gdb 启用整齐打印",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "make debug"
}
]
}

这里我配置了两个启动选项, 一个直接运行release程序, 另一个运行debug程序, 这里针对debug启动项进行解释说明

name : 我们在启动选项卡里看到的启动项名称

type : cppdbg就可以, 具体可以查阅vscode官方说明

request : 启动项类型, 一种是附加程序一种是直接启动, 这里是直接启动

program : 启动程序路径, 在vscode里打开的根目录即为${workspaceFolder}, 后面加上release路径

args : 传入程序的参数

stopAtEntry : 程序是否自动在入口暂停, debug版才有用哦

cwd : 程序运行时的目录

environment :要添加到程序环境中的环境变量, 具体可以查阅vscode官方说明, 这里我直接没填

externalConsole : 选择程序是在新的控制台中启动还是在集成控制台启动

MIMode : 调试器选择

setupCommands : vscode官方文档查, 这里我是直接用默认配置的

preLaunchTask : 这个是最重要的选项了, 该选项指明了在运行当前选项卡之前要运行的task任务, 这个task任务配置在同目录下的tasks.json中, 这里填的内容是task的label

为了解释preLaunchTask这个选项, 我们引入tasks.json, 这里贴出我的tasks.json, 进行说明

 {
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "make release",
"command": "make",
"args": [],
"options": {
"cwd": "${workspaceFolder}/project"
},
"group": "build"
},
{
"type": "shell",
"label": "make debug",
"command": "make -f debug.makefile",
"args": [],
"options": {
"cwd": "${workspaceFolder}/project"
},
"group": "build"
},
{
"type": "shell",
"label": "make clean",
"command": "make",
"args": ["clean_all"],
"options": {
"cwd": "${workspaceFolder}/project"
},
"group": "build"
},
{
"type": "shell",
"label": "make clean debug",
"command": "make -f debug.makefile",
"args": ["clean_all"],
"options": {
"cwd": "${workspaceFolder}/project"
},
"group": "build"
},
{
"type": "shell",
"label": "make clean cache",
"command": "make",
"args": ["clean_all_cache"],
"options": {
"cwd": "${workspaceFolder}/project"
},
"group": "build"
}
]
}

在这个文件中我配置了5个task, 其中前2个task : make release 和 make debug用于执行不同的makefile

这里我针对make debug做个简单说明

type : task的类型, 这里填shell相当于执行shell命令

label : task的名字

command : 要执行的指令, 这里要注意 make -f xxx.file这种命令, -f xxx这个内容要直接写到命令内容中, 而不能写到下面的args里, 会无法识别, 这里大家可以自行验证一下

args : command要附加的参数

options : 其他选项

cwd : task执行的目录

group : task的分组, 可以查一下vscode官方说明

经过以上配置, vscode就和我们的makefile联系在一起了, 选好启动项f5就完事了, 这里我贴出我的test.cpp, test.h, 和vscode断点调试运行截图

 #include "test.h"

 int main(int argc, char* argv[])
{
if (argc > )
{
std::cout << "input param : ";
for (int idx = ; idx < argc; ++idx)
{
std::cout << argv[idx] << " ";
}
std::cout << std::endl;
} std::cout << std::endl << "using a" << std::endl;
std::cout << tsi::a1st::lib_name() << std::endl
<< tsi::a2nd::lib_author() << std::endl
<< tsi::a3rd::lib_version() << std::endl; std::cout << std::endl << "using so" << std::endl;
std::cout << tsi::so1st::lib_name() << std::endl
<< tsi::so2nd::lib_author() << std::endl
<< tsi::so3rd::lib_version() << std::endl;
return ;
}
 #ifndef _TSI_TEST_
#define _TSI_TEST_ #include <iostream> #include "a_1st.h"
#include "a_2nd.h"
#include "a_3rd.h"
#include "so_1st.h"
#include "so_2nd.h"
#include "so_3rd.h" #endif

这样, 一个简单的项目工程的开发环境就搭建成功了. PS: 在调试时, 我遇到了一个小问题, 这里也贴一下

这里一开始我无法进行gdb调试, 提示无法读取文件云云, 点击创建后, 有了上述提示, 在网上检索了一下, 只有解决方案, 没有详细解释其中机理, 这里我先贴出解决办法

在/目录下建立build目录,在该目录中建立错误提示中对应的目录, 并下载提示对应版本glibc到目录中并解压即可解决问题

关于该错误我认为是gdb调试加载路径错误导致, 如果有了解详细原因的朋友, 请务必留言指点, 在此谢过

以上, 上方示例只是一个简单的项目结构, 其中一定还有很多不足之处, 本文仅起到一个抛砖引玉的作用, 如有错误疏漏, 请务必指出, 有问题欢迎讨论, 转载注明出处, 感谢

linux下使用vscode和makefile搭建C++开发环境的更多相关文章

  1. linux(ubuntu)和windows下面快速搭建android开发环境

    在windows和linux下面搭建android开发环境,一般要安装以下几个软件: 1.JDK安装 2.Eclipse安装 3.Android SDK安装 4.在eclipse里面安装ADT 5.e ...

  2. Linux(ubuntu 12.04桌面版) 搭建Android开发环境

    因为一些工作上的原因,需要切换到Linux环境下做点开发,我选择的Linux发行版本为ubuntu(我不建议使用fedora,我最开始就是使用的fedora,但发现并不是特别好使,有些插件没办法安装, ...

  3. Linux(4)- centos7安装python3、Linux下安装、配置virtualenv、确保开发环境的一致性、虚拟环境之virtualenvwrapper、vim

    一.centos7安装python3 1.下载python3的源码包 下载地址:https://www.python.org/ftp/python/3.6.2/Python-3.6.2.tgz cd ...

  4. 使用vsCode配合IAR搭建arm开发环境

    众所周知IAR的编辑功能就是个垃圾,但是不得不承认IAR的编译器相当的牛X,经常以稳定可靠而著称,为此我们把VSCODE强大的编辑功能和IAR结合一下来加快我们的开发周期. 一.下载VSCODE并安装 ...

  5. Linux环境下使用VSCode编译makefile文件的注意事项

    Linux环境下使用VSCode编译makefile文件的注意事项 首先安装C/C++的两个依赖 在debug,launch会自动的生成下方的launch.json launch.json { // ...

  6. Linux下快速搭建php开发环境

    php开发环境快速搭建 一.Linux下快速搭建php开发环境 1.安装XAMPP for Linux XAMPP(Apache+MySQL+PHP+PERL)是一个功能强大的建站集成软件包,使用XA ...

  7. Linux环境下搭建Android开发环境

    最近在折腾linux.因为咱是搞安卓开发的,所以少不了需要搭建Android开发环境,就此小记,希望能给向我一样的开发者一点帮助!开干! 1.安装JDK 下载JDK包,得到的是类似于jdk-8u65- ...

  8. Ubuntu 12.04下搭建Qt开发环境

    http://download.qt.io/official_releases/qt/ Ubuntu 环境下Gtk与Qt编译环境安装与配置(系统环境是Ubuntu 12.04) 1.配置基础开发环境G ...

  9. ubuntu下搭建JAVA开发环境【转】

    转自:http://jingyan.baidu.com/article/86fae346b696633c49121a30.html JAVA开发环境是一种跨平台的程序设计语言,可以在windows.L ...

  10. Windows下visual studio code搭建golang开发环境

    Windows下visual studio code搭建golang开发环境 序幕 其实环境搭建没什么难的,但是遇到一些问题,主要是有些网站资源访问不了(如:golang.org),导致一些包无法安装 ...

随机推荐

  1. webView、scrollView、TableView,为了防止滚动时出现偏移,底部黑框问题等

    if ([self respondsToSelector:@selector(automaticallyAdjustsScrollViewInsets)]) {self.automaticallyAd ...

  2. Linux字符设备中的两个重要结构体(file、inode)

    对于Linux系统中,一般字符设备和驱动之间的函数调用关系如下图所示 上图描述了用户空间应用程序通过系统调用来调用程序的过程.一般而言在驱动程序的设计中,会关系 struct file 和 struc ...

  3. how tomcat works 读书笔记(一)----------一个简单的webserver

    http协议 若是两个人能正常的说话交流,那么他们间必然有一套统一的语言规则<在网络上server与client能交流也依赖与一套规则,它就是我们说的http规则(超文本传输协议Hypertex ...

  4. Silverlight 结合ArcGis 使用inforwindow

    原文 http://www.dotblogs.com.tw/justforgood/archive/2012/05/10/72089.aspx 也许有些人不知道什么事inforwindow,简单来说就 ...

  5. Ubuntu 12.04下PHP环境的搭建(LAMP)

    1.首先打开命令行,切换到root身份,获得最新的软件包 su root sudo apt-get install update 2.安装MySQL数据库 sudo apt-get install m ...

  6. pycharm 2017新建文件添加编码方式等

    file->setting->Editor->File and Code Templates->Python Script 添加 #!/usr/bin/python3# -*- ...

  7. Effective Java 第三版——2. 当构造方法参数过多时使用builder模式

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  8. django——文本编辑器

    在博客项目中,为了支持用户的在线编辑博客,我们选用了kindeditor这个强大的编辑器. 以下是对kindeditor的简介,以及在Django中引入这个编辑器的方法:) 1.KindEditor是 ...

  9. Java容器解析系列(6) Queue Deque AbstractQueue 详解

    首先我们来看一下Queue接口: /** * @since 1.5 */ public interface Queue<E> extends Collection<E> { / ...

  10. 【HTML打印】HTML直接调用window下的打印机并执行打印任务(简单打印任务生成)

    1.<button onclick="preview('data');" id="print">打印</button> 2. 3.js: ...