Qida's Blog

纸上得来终觉浅,绝知此事要躬行。

国外

ICANN

ICANN (The Internet Corporation for Assigned Names and Numbers) 互联网域名与数字地址分配机构

https://www.icann.org/

互联网名称与数字地址分配机构 (ICANN) 简介

ICANN

IANA

IANA (Internet Assigned Numbers Authority) 互联网号码分配局,由非营利机构 ICANN 管理。

https://www.iana.org/

<The History of IANA: An Extended Timeline with Citations and Commentary>

IANA 的职责:

tz 数据库由 David Olson 创立,收集了自 1970 年以来被广泛认可的民用时钟的时区信息。2011 年,ICANN 接管了这个被全球电脑和网站广泛使用的时区数据库,该机构通常只赞助对互联网发展非常重要的项目。

现在,具体的维护工作由 IANA 负责。Paul Eggert 是时区数据库的项目负责人,该职位被称为 TZ 协调员。

ISCO

ISCO (Internet Society) 互联网协会

https://www.internetsociety.org/

IAB

IAB (Internet Architecture Board) 互联网架构委员会

https://www.iab.org/

IETF

IETF (Internet Engineering TaskForce) 互联网工程任务组

https://www.ietf.org/

https://www.ietf.org/standards/

https://www.ietf.org/standards/rfcs/

IETF

IEC

IEC (International Electrotechnical Commission) 国际电工委员会

https://en.wikipedia.org/wiki/International_Electrotechnical_Commission

https://en.wikipedia.org/wiki/List_of_International_Electrotechnical_Commission_standards

国内

CNNIC

CNNIC 中国互联网信息中心

http://www.cnnic.net.cn/

CNNIC

注册域名、管理资源记录(RR)都是站长/运维最常见的操作。本文总结了相关知识。

DNS 是什么?

DNS 是互联网的一项基础服务,它将人类易记的域名解析为不易记的 IP 地址,使人更方便的访问互联网。

DNS 的结构?

域名系统(DNS)是一个多层级、分布式的系统,就如同一个树状结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
                    +---+                                 
| . | Root domain
+-+-+
|
+-------+-------+---+---+-------+-------+
| | | | | |
+-+-+ +-+-+ +-+-+ +-+-+ +-+-+ +-+-+
|com| |net| |org| |gov| |cn | |...| Top-level domain (TLD)
+---+ +---+ +-+-+ +---+ +---+ +---+
|
+----+----+
|wikipedia| Second-level domain (SLD)
+----+----+
|
+---------------+
| | |
+-+-+ +-+-+ +-+--+
|www| |ftp| |mail| Resource record (RR)
+---+ +---+ +----+
Domain types DNS zone Name server
Root domain
根域名
DNS root zone
DNS 根区
Root name server
根域名服务器
Top-level domain (TLD)
顶级域名
DNS zone TLD name server
顶级域名服务器
Second-level domain (SLD)
二级域名
DNS zone Authoritative name server
权威域名服务器
Sub domain
子域名
Resource recerd, RR
资源记录

域名系统(DNS)的每一级只知道直接下级的位置,而无法获得跨级的位置,因此在域名解析的时候,需要自上而下、逐级查询。这种机制虽然看似低效,却能够提供分布式、高容错的服务,避免让域名系统(DNS)成为一个集中式的单点系统。

根域名(Root Domain)

早期的域名必须以英文句号“.”结尾,当用户访问 www.wikipedia.org 的 HTTP 服务时必须在地址栏中输入:http://www.wikipedia.org.,这样 DNS 才能够进行域名解析。如今 DNS 服务器已经可以自动补上结尾的句号。这个点 . 就是根域名。

. 代表的根域名服务器(Root name server),是 DNS 中最高级别的域名服务器(name server),负责返回顶级域名的权威域名服务器(Authoritative name server)的地址。

由于 ICANN 管理着所有的顶级域名,所以它是最高一级的域名节点。

根域名服务器(Root Name Server)

https://www.iana.org/domains/root/servers

由于早期的 DNS 查询结果是一个 512 字节的 UDP 数据包。这个包最多可以容纳 13 个服务器的地址,因此就规定全世界有 13 个根域名服务器,编号从 a.root-servers.net 一直到 m.root-servers.net

HOSTNAME IP ADDRESSES OPERATOR
a.root-servers.net 198.41.0.4, 2001:503:ba3e::2:30 Verisign, Inc.
b.root-servers.net 199.9.14.201, 2001:500:200::b University of Southern California, Information Sciences Institute
c.root-servers.net 192.33.4.12, 2001:500:2::c Cogent Communications
d.root-servers.net 199.7.91.13, 2001:500:2d::d University of Maryland
e.root-servers.net 192.203.230.10, 2001:500:a8::e NASA (Ames Research Center)
f.root-servers.net 192.5.5.241, 2001:500:2f::f Internet Systems Consortium, Inc.
g.root-servers.net 192.112.36.4, 2001:500:12::d0d US Department of Defense (NIC)
h.root-servers.net 198.97.190.53, 2001:500:1::53 US Army (Research Lab)
i.root-servers.net 192.36.148.17, 2001:7fe::53 Netnod
j.root-servers.net 192.58.128.30, 2001:503:c27::2:30 Verisign, Inc.
k.root-servers.net 193.0.14.129, 2001:7fd::1 RIPE NCC
l.root-servers.net 199.7.83.42, 2001:500:9f::42 ICANN
m.root-servers.net 202.12.27.33, 2001:dc3::35 WIDE Project

这 13 台根域名服务器由 12 个组织独立运营。其中,Verisign 公司管理两台根域名服务器:A 和 J。每家公司为了保证根域名服务器的可用性,会部署多个节点,比如单单 Verisign 一家公司就部署了 104 台根域名服务器(2016 年 1 月数据)。

所以,根域名服务器其实不止 13 台。据统计,截止 2016 年 1 月,全世界共有 517 台根域名服务器。你可以在 https://root-servers.org/ 这个网站查到所有根域名服务器的信息。

编号相同的根域名服务器使用同一个 IP,数百台根域名服务器总共只使用 13 个 IP(如上表所示),因此可以抵抗针对其所进行的分布式拒绝服务攻击(DDoS)。

这些根域名服务器的运行软件皆为 BINDNSD

根提示文件(Root Hints File)

https://www.internic.net/domain/named.root

根域名服务器虽然有 13 个域名,但是最少必须知道一台的 IP 地址,否则就会陷入循环查询。一般来说,本机都保存一份根域名服务器的 IP 地址的缓存,叫做 named.root 文件。这个文件同时记录了 13 台根域名服务器的 IP 地址。

1
2
3
4
5
6
7
8
9
.                        3600000      NS    A.ROOT-SERVERS.NET.
A.ROOT-SERVERS.NET. 3600000 A 198.41.0.4
A.ROOT-SERVERS.NET. 3600000 AAAA 2001:503:ba3e::2:30

. 3600000 NS B.ROOT-SERVERS.NET.
B.ROOT-SERVERS.NET. 3600000 A 199.9.14.201
B.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:200::b

...

参考:4.1 Address resolution mechanism

For proper operation of its domain name resolver, a network host is configured with an initial cache (hints) of the known addresses of the root name servers. The hints are updated periodically by an administrator by retrieving a dataset from a reliable source.

域名解析的循环依赖问题及其解决方案:4.3 Circular dependencies and glue records

Name servers in delegations are identified by name, rather than by IP address. This means that a resolving name server must issue another DNS request to find out the IP address of the server to which it has been referred. If the name given in the delegation is a subdomain of the domain for which the delegation is being provided, there is a circular dependency.

In this case, the name server providing the delegation must also provide one or more IP addresses for the authoritative name server mentioned in the delegation. This information is called glue. The delegating name server provides this glue in the form of records in the additional section of the DNS response, and provides the delegation in the authority section of the response. A glue record is a combination of the name server and IP address.

For example, if the authoritative name server for example.org is ns1.example.org, a computer trying to resolve www.example.org first resolves ns1.example.org. As ns1 is contained in example.org, this requires resolving example.org first, which presents a circular dependency. To break the dependency, the name server for the top level domain org includes glue along with the delegation for example.org. The glue records are address records that provide IP addresses for ns1.example.org. The resolver uses one or more of these IP addresses to query one of the domain’s authoritative servers, which allows it to complete the DNS query.

根区文件(Root Zone File)

https://www.internic.net/domain/root.zone

DNS 根域名服务器(Root name server)负责维护 DNS 根区文件(Root zone file)。该文件保存了所有顶级域名(TLD)的托管信息,所以非常大,超过 2MB。

举例来说,顶级域名 .com 可以查到 13 个域名服务器:

1
2
3
4
5
6
7
8
9
10
11
12
13
com.            172800  IN  NS  a.gtld-servers.net.
com. 172800 IN NS b.gtld-servers.net.
com. 172800 IN NS c.gtld-servers.net.
com. 172800 IN NS d.gtld-servers.net.
com. 172800 IN NS e.gtld-servers.net.
com. 172800 IN NS f.gtld-servers.net.
com. 172800 IN NS g.gtld-servers.net.
com. 172800 IN NS h.gtld-servers.net.
com. 172800 IN NS i.gtld-servers.net.
com. 172800 IN NS j.gtld-servers.net.
com. 172800 IN NS k.gtld-servers.net.
com. 172800 IN NS l.gtld-servers.net.
com. 172800 IN NS m.gtld-servers.net.

也就是说,.com 域名的解析结果,可以到这个 13 个服务器的任一台查询。细心的读者可能发现,这些服务器本身也是使用域名(比如 a.gtld-servers.net.)标识,那么还得去查询它们指向的服务器,这样很容易造成循环查询。

因此,DNS 根区文件还会同时提供这些服务器的 IP 地址(IPv4 和 IPv6):

1
2
3
4
5
6
7
a.gtld-servers.net. 172800  IN  A     192.5.6.30
a.gtld-servers.net. 172800 IN AAAA 2001:503:a83e:0:0:0:2:30
b.gtld-servers.net. 172800 IN A 192.33.14.30
b.gtld-servers.net. 172800 IN AAAA 2001:503:231d:0:0:0:2:30
c.gtld-servers.net. 172800 IN A 192.26.92.30
c.gtld-servers.net. 172800 IN AAAA 2001:503:83eb:0:0:0:0:30
...

理论上,所有域名查询都必须先查询根域名,因为只有根域名才能告诉你,某个顶级域名由哪个运营商、哪台服务器管理。事实上也确实如此,ICANN 维护着根区文件(Root zone file),里面记载着顶级域名和对应的记录。

但由于该文件很少变化,大多数 DNS 服务商都会提供它的缓存,所以根域名的查询事实上不是那么频繁。

根区数据库(Root Zone Database)

https://www.iana.org/domains/root/db

提供顶级域名的授权信息。

Much of this data is also available via the WHOIS protocol at whois.iana.org.

顶级域名(Top-Level Domain)

所有顶级域名保存在 Top-Level Domain List,可查询根区数据库(Root Zone Database)以获得更多信息(如顶级域名运营商信息、Name Servers)。

顶级域名(TLD)分为如下六种类型:

As of 2015, IANA distinguishes the following groups of top-level domains:[13]

  1. Infrastructure top-level domain (ARPA): This group consists of one domain, the Address and Routing Parameter Area. It is managed by IANA on behalf of the Internet Engineering Task Force for various purposes specified in the Request for Comments publications.

  2. Generic top-level domains (gTLD): Top-level domains with three or more characters

  3. restricted generic top-level domains (grTLD): These domains are managed under official ICANN accredited registrars.

  4. Sponsored top-level domains (sTLD): These domains are proposed and sponsored by private agencies or organizations that establish and enforce rules restricting the eligibility to use the TLD. Use is based on community theme concepts; these domains are managed under official ICANN accredited registrars.

  5. country-code top-level domains (ccTLD): Two-letter domains established for countries or territories. With some historical exceptions, the code for any territory is the same as its two-letter ISO 3166 code.

  1. Test top-level domains (tTLD): These domains were installed under .test for testing purposes in the IDN development process; these domains are not present in the root zone.

其中下面两类是最常用的:

顶级域名的数量仍在不断增长中,除了英文字母的域名,还不断新增各种语系的域名,如中文域名。

顶级域名服务器(TLD name server)

二级域名(Second-Level Domain)

组织或个人通过域名代理服务商(如 GoDaddy、万网)进行注册的域名。根据需要还可以自行在二级域名下新增三级、四级等子域名

权威域名服务器(Authoritative name server)

https://en.wikipedia.org/wiki/Name_server#Authoritative_name_server

任何一个拥有域名的主机,其域名与 IP 地址的映射关系等信息都存储在权威域名服务器上。

资源记录(Resource Record)

域名系统中,一般一个域(DNS zone)通过一个 zone 文件保存该域的相关配置信息。zone 文件包含了域名和 IP 地址等资源之间的映射,以资源记录(Resource recerd, RR)的文本形式进行组织。

这里列举了所有的资源记录类型:

DNS record types

DNS record types

以域名 example.com 为例,其 zone 文件简化如下:

name ttl record class record type record data comment
example.com. 1h IN NS ns ns.example.com is a nameserver for example.com
ns 1h IN A 192.0.2.2 IPv4 address for ns.example.com
example.com. 1h IN A 192.0.2.1 IPv4 address for example.com
www 1h IN CNAME example.com. www.example.com is an alias for example.com

CDN 服务常用到 CNAME 记录。

域名解析方式

参考 RFC 1034: Domain Names - Concepts and Facilities - IETF 规范的 2. INTRODUCTION - 2.3. Assumptions about usage 对于两种域名解析方式的描述:

In any system that has a distributed database, a particular name server may be presented with a query that can only be answered by some other server. The two general approaches to dealing with this problem are

  • “recursive”, in which the first server pursues the query for the client at another server,
  • “iterative”, in which the server refers the client to another server and lets the client pursue the query.

Both approaches have advantages and disadvantages, but the iterative approach is preferred for the datagram style of access. The domain system requires implementation of the iterative approach, but allows the recursive approach as an option.

迭代方式(Iterative Approach)

Iterative Approach

A DNS resolver that implements the iterative approach mandated by RFC 1034; in this case, the resolver consults three name servers to resolve the fully qualified domain namewww.wikipedia.org".

递归方式(Recursive Approach)

DNS resolution

公共域名服务器

https://en.wikipedia.org/wiki/Public_recursive_name_server

https://public-dns.info/

DNS 相关命令

dig

dig

安装

dig 命令安装:

1
$ apt-get install dnsutils

使用

dig 命令使用:https://downloads.isc.org/isc/bind9/cur/9.17/doc/arm/html/manpages.html#dig-dns-lookup-utility

1
$ dig @name_server name record_type

其中,record_type 参数如下:https://en.wikipedia.org/wiki/List_of_DNS_record_types

DNS record types

例子

dig 命令使用例子:

1
2
# To use a specific DNS server for the query, use the @ option.
$ dig @8.8.8.8 example.com
1
2
3
# By default, dig displays the A record for a domain. To look up a different DNS record, add it to the end of the command. 
# For example, to look up the MX (mail exchanger) record for the example.com domain, type the following command:
$ dig example.com MX
1
2
3
4
5
# query for any type of record information
$ dig +noall +answer www.google.com any

www.google.com. 107 IN A 142.250.66.132
www.google.com. 94 IN AAAA 2404:6800:4005:802::2004

+[no]trace

This option toggles tracing of the delegation path from the root name servers for the name being looked up. Tracing is disabled by default. When tracing is enabled, dig makes iterative queries to resolve the name being looked up. It follows referrals from the root servers, showing the answer from each server that was used to resolve the lookup.

下例中,使用公共域名服务器,而不是依次使用 /etc/resolv.conf 里配置的本地域名服务器进行 DNS 迭代查询,结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
$ dig @8.8.8.8 +trace www.google.com

; <<>> DiG 9.10.6 <<>> @8.8.8.8 +trace www.google.com
; (1 server found)
;; global options: +cmd
. 24814 IN NS m.root-servers.net.
. 24814 IN NS b.root-servers.net.
. 24814 IN NS c.root-servers.net.
. 24814 IN NS d.root-servers.net.
. 24814 IN NS e.root-servers.net.
. 24814 IN NS f.root-servers.net.
. 24814 IN NS g.root-servers.net.
. 24814 IN NS h.root-servers.net.
. 24814 IN NS a.root-servers.net.
. 24814 IN NS i.root-servers.net.
. 24814 IN NS j.root-servers.net.
. 24814 IN NS k.root-servers.net.
. 24814 IN NS l.root-servers.net.
;; Received 525 bytes from 8.8.8.8#53(8.8.8.8) in 157 ms

com. 172800 IN NS b.gtld-servers.net.
com. 172800 IN NS e.gtld-servers.net.
com. 172800 IN NS i.gtld-servers.net.
com. 172800 IN NS j.gtld-servers.net.
com. 172800 IN NS a.gtld-servers.net.
com. 172800 IN NS c.gtld-servers.net.
com. 172800 IN NS d.gtld-servers.net.
com. 172800 IN NS k.gtld-servers.net.
com. 172800 IN NS m.gtld-servers.net.
com. 172800 IN NS h.gtld-servers.net.
com. 172800 IN NS f.gtld-servers.net.
com. 172800 IN NS l.gtld-servers.net.
com. 172800 IN NS g.gtld-servers.net.
;; Received 1174 bytes from 202.12.27.33#53(m.root-servers.net) in 48 ms

google.com. 172800 IN NS ns2.google.com.
google.com. 172800 IN NS ns1.google.com.
google.com. 172800 IN NS ns3.google.com.
google.com. 172800 IN NS ns4.google.com.
;; Received 840 bytes from 192.48.79.30#53(j.gtld-servers.net) in 196 ms

www.google.com. 300 IN A 142.250.66.100
;; Received 59 bytes from 216.239.38.10#53(ns4.google.com) in 84 ms

Wireshark 网卡抓包截图如下:DNS 协议 基于 UDP 协议,使用 53 端口。

DNS 协议

nslookup

nslookup 命令:https://downloads.isc.org/isc/bind9/cur/9.17/doc/arm/html/manpages.html#nslookup-query-internet-name-servers-interactively

1
2
3
4
5
6
7
8
$ nslookup www.google.com

Server: 10.0.20.101
Address: 10.0.20.101#53

Non-authoritative answer:
Name: www.google.com
Address: 142.250.66.132

参考

RFC 1034: Domain Names - Concepts and Facilities - IETF

RFC 1035 - Domain names - implementation and specification

RFC 1912 - 常见的 DNS 操作和配置错误

https://www.iana.org/domains/root

https://en.wikipedia.org/wiki/Domain_Name_System

https://en.wikipedia.org/wiki/Domain_name

根域名的知识 - 阮一峰

https://wizardzines.com/comics/dig/

https://wizardzines.com/comics/dns-record-types/

如果美国封了 DNS,俄罗斯将从网络消失?

美国能让中国从网络上消失?- 良许 Linux

Eureka 服务注册发现流程

eureka workflow

eureka cluster

eureka config

Eureka 配置修改

Eureka client (consumer)

1
2
3
4
5
6
7
# Indicates whether this client should fetch eureka registry information from eureka server.
# 指示此 eureka client 是否应从 eureka server 获取注册表信息。(缓存到本地,后面会增量获取)
eureka.client.fetch-registry=true

# Indicates how often(in seconds) to fetch the registry information from the eureka server.
# 指示此 eureka client 从 eureka server 获取注册表信息的频率(以秒为单位)。可用于 eureka client 下线后,快速感知
eureka.client.registry-fetch-interval-seconds=30

Eureka client (provider)

1
2
3
4
5
6
7
8
9
10
# Map of availability zone to list of fully qualified URLs to communicate with eureka server. Each value can be a single URL or a comma separated list of alternative locations. Typically the eureka server URLs carry protocol,host,port,context and version information if any. Example: http://ec2-256-156-243-129.compute-1.amazonaws.com:7001/eureka/ The changes are effective at runtime at the next service url refresh cycle as specified by eurekaServiceUrlPollIntervalSeconds.
eureka.client.service-url.default-zone: http://localhost:8000/eureka/

# Indicates whether or not this instance should register its information with eureka server for discovery by others. In some cases, you do not want your instances to be discovered whereas you just want do discover other instances.
# 指示此实例是否应向 eureka server 注册其信息以供其它服务发现。两种情况下会设置为 false:1、您不希望您的实例被发现,而您只想发现其他实例。2、eureka server 以单机模式运行(https://cloud.spring.io/spring-cloud-netflix/reference/html/#spring-cloud-eureka-server-standalone-mode)
eureka.client.register-with-eureka=true

# Indicates how often (in seconds) the eureka client needs to send heartbeats to eureka server to indicate that it is still alive. If the heartbeats are not received for the period specified in leaseExpirationDurationInSeconds, eureka server will remove the instance from its view, there by disallowing traffic to this instance. Note that the instance could still not take traffic if it implements HealthCheckCallback and then decides to make itself unavailable.
# 服务续约时间(心跳时间) —— 指示 eureka client 需要多长时间(以秒为单位)向 eureka server 发送心跳以表明它仍然活着。如果 eureka server 在 lease-expiration-duration-in-second 中指定的时间段内未收到心跳,eureka server 将从其视图中删除该实例,从而限流该实例。请注意,如果实例实现 HealthCheckCallback 然后决定使其自身不可用,则该实例仍然无法获取流量。
eureka.instance.lease-renewal-interval-in-seconds=30

Eureka server

1
2
3
4
5
6
7
8
9
10
11
# Indicates the time in seconds that the eureka server waits since it received the last heartbeat before it can remove this instance from its view and there by disallowing traffic to this instance. Setting this value too long could mean that the traffic could be routed to the instance even though the instance is not alive. Setting this value too small could mean, the instance may be taken out of traffic because of temporary network glitches.This value to be set to atleast higher than the value specified in leaseRenewalIntervalInSeconds.
# 服务过期时间 —— 指示 eureka server 自收到最后一次心跳后等待的时间(以秒为单位),然后才能从其视图中删除此实例,并限流该实例。将此值设置得太长可能意味着即使实例挂了,流量也可以路由到实例。将此值设置得太小可能意味着,由于临时网络故障,eureka server 未及时收到心跳,实例可能会被限流。此值至少要设置为高于 lease-renewal-interval-in-seconds 中指定的值。
eureka.instance.lease-expiration-duration-in-second=90

# Gets the time interval with which the task that expires instances should wake up and run.
# 服务剔除 TimerTask 执行时间
eureka.server.eviction-interval-timer-in-ms=60 * 1000

# Gets the time interval with which the payload cache of the client should be updated.
# 一级缓存更新 TimerTask 执行时间,定时将 L2(readWriteCacheMap)覆盖掉 L1(readOnlyCacheMap)
eureka.server.response-cache-update-interval-ms=30 * 1000

Eureka 配置验证

配置修改后,可以请求端点:/actuator/configprops,验证配置,例如:

Eureka server

查找关键字:responseCacheUpdateIntervalMs:

eureka server configprops

Eureka client

Eureka client (Consumer) 查询关键字:registryFetchIntervalSeconds

eureka client configprops

Eureka client (Consumer) 可以请求端点:/actuator/health 验证服务下线的感知结果:

eureka client health

Eureka client 接口上下线

Eureka client 服务下线接口:/actuator/service-registry?status=DOWN

1
2
curl --location --request POST 'http://127.0.0.1:58061/actuator/service-registry?status=DOWN' \
--header 'Content-Type: application/vnd.spring-boot.actuator.v2+json;charset=UTF-8'

eureka client down

Eureka client 服务上线接口:/actuator/service-registry?status=UP

1
2
curl --location --request POST 'http://127.0.0.1:58061/actuator/service-registry?status=UP' \
--header 'Content-Type: application/vnd.spring-boot.actuator.v2+json;charset=UTF-8'

eureka client up

Eureka server 验证结果:

1
2
curl --location --request GET 'http://192.168.33.42:8000/eureka/apps' \
--header 'Accept: application/json'

Eureka client 手动上下线

eureka client down and up

参考

解决微服务架构下流量有损问题,优雅上下线的实践和探索 | 阿里技术

eureka client 下线后,快速感知

C 语言

C++ integrates the operators new and delete for allocating dynamic memory. But these were not available in the C language; instead, it used a library solution, with the functions malloc, calloc, realloc and free, defined in the header <cstdlib> (known as <stdlib.h> in C). The functions are also available in C++ and can also be used to allocate and deallocate dynamic memory.

Note, though, that the memory blocks allocated by these functions are not necessarily compatible with those returned by new, so they should not be mixed; each one should be handled with its own set of functions or operators.

动态内存分配 malloc, calloc, realloc

https://www.runoob.com/cprogramming/c-memory-management.html

动态内存回收 free

https://www.runoob.com/cprogramming/c-memory-management.html

C++ 语言

动态内存分配 new

在 C++ 中,您可以使用特殊的运算符为给定类型的变量在运行时分配堆内存(memory heap),这会返回所分配的空间地址

动态内存分配使用 new 运算符,后面跟上一个数据类型,语法如下:

1
2
3
4
5
6
// allocate memory to contain one single element of specified type
pointer = new type

// allocate a block (an array) of elements of specified type, where `number_of_elements` is an integer value representing the amount of these.
// it returns a pointer to the beginning of the new block of memory allocated.
pointer = new type [number_of_elements]

例如:

1
2
int * bar = new int(5); // int bar = 5
int * foo = new int[5]; // int foo[5]

In this case, the system dynamically allocates space for five elements of type int and returns a pointer to the first element of the sequence, which is assigned to foo (a pointer). Therefore, foo now points to a valid block of memory with space for five elements of type int.

Here, foo is a pointer, and thus, the first element pointed to by foo can be accessed either with the expression foo[0] or the expression *foo (both are equivalent). The second element can be accessed either with foo[1] or *(foo+1), and so on…

由于使用动态内存分配机制,因此 number_of_elements 可以是一个变量,变量值在运行时才决定,例如:p = new int[i];


声明普通数组与使用 new 分配动态内存的区别:

There is a substantial difference between declaring a normal array and allocating dynamic memory for a block of memory using new. The most important difference is that the size of a regular array needs to be a constant expression, and thus its size has to be determined at the moment of designing the program, before it is run, whereas the dynamic memory allocation performed by new allows to assign memory during runtime using any variable value as size.


C++ 提供了两种标准机制来检查堆内存分配是否成功:

The dynamic memory requested by our program is allocated by the system from the memory heap. However, computer memory is a limited resource, and it can be exhausted. Therefore, there are no guarantees that all requests to allocate memory using operator new are going to be granted by the system.

C++ provides two standard mechanisms to check if the allocation was successful:

机制一:异常机制

One is by handling exceptions. Using this method, an exception of type bad_alloc is thrown when the allocation fails. If this exception is thrown and it is not handled by a specific handler, the program execution is terminated.

1
foo = new int [5];  // if allocation fails, an exception is thrown

机制二:返回空指针

The other method is known as nothrow, and what happens when it is used is that when a memory allocation fails, instead of throwing a bad_alloc exception or terminating the program, the pointer returned by new is a null pointer, and the program continues its execution normally.

This method can be specified by using a special object called nothrow, declared in header <new>, as argument for new:

1
foo = new (nothrow) int [5];

In this case, if the allocation of this block of memory fails, the failure can be detected by checking if foo is a null pointer:

1
2
3
4
5
int * foo;
foo = new (nothrow) int [5];
if (foo == nullptr) {
// error assigning memory. Take measures.
}

This nothrow method is likely to produce less efficient code than exceptions, since it implies explicitly checking the pointer value returned after each and every allocation. Therefore, the exception mechanism is generally preferred, at least for critical allocations. But nothrow mechanism is more simplicity.

It is considered good practice for programs to always be able to handle failures to allocate memory, either by checking the pointer value (if nothrow) or by catching the proper exception.

动态内存回收 delete

如果您不再需要动态分配的内存空间,可以使用 delete 运算符,删除之前由 new 运算符分配的堆内存(memory heap),以便该内存可再次用于其它动态内存分配。语法如下:

1
2
3
4
5
// releases the memory of a single element allocated using new
delete bar;

// releases the memory allocated for arrays of elements using new and a size in brackets ([])
delete [] foo; // 不管所删除数组的维数多少,指针名前只用一对方括号 []

参考

http://www.cplusplus.com/doc/tutorial/dynamic/

Input/Output library

下图是 C++ 提供的输入/输出库,其中:

  • <xxx> 表示:头文件
  • 白框表示:类(Classes)
  • 黑框表示:对象(Objects)

Input/Output library

The iostream library is an object-oriented library that provides input and output functionality using streams.

A stream is an abstraction that represents a device on which input and ouput operations are performed. A stream can basically be represented as a source or destination of characters of indefinite length.

Streams are generally associated to a physical source or destination of characters, like a disk file, the keyboard, or the console, so the characters gotten or written to/from our abstraction called stream are physically input/output to the physical device. For example, file streams are C++ objects to manipulate and interact with files; Once a file stream is used to open a file, any input or output operation performed on that stream is physically reflected in the file.

To operate with streams, C++ provides the standard iostream library, which contains the following elements:

Elements of the iostream library

Basic class templates

The base of the iostream library is the hierarchy of class templates. The class templates provide most of the functionality of the library in a type-independent fashion.

Class template instantiations

The library incorporates two standard sets of instantiations of the entire iostream class template hierarchy:

  • The narrow-oriented (char type) instantiation is probably the better known part of the iostream library. Classes like ios, istream and ofstream are narrow-oriented. The diagram on top of this page shows the names and relationships of narrow-oriented classes.

  • The classes of the wide-oriented (wchar_t) instatiation follow the same naming conventions as the narrow-oriented instantiation but with the name of each class and object prefixed with a w character, forming wios, wistream and wofstream, as an example.

Objects

As part of the iostream library, the header file <iostream> declares certain objects that are used to perform input and output operations on the standard input and output.

They are divided in two sets:

  • narrow-oriented objects: cin, cout, cerr and clog
  • wide-oriented objects: wcin, wcout, wcerr and wclog

Manipulators

Manipulators are global functions designed to be used together with insertion (<<) and extraction (>>) operators performed on iostream stream objects. They generally modify properties and formatting settings of the streams.

常用头文件

下列这些头文件在 C++ 编程中很常用,下面分别介绍:

头文件 函数和描述
<iostream> 该文件定义了 cincoutcerrclog 对象,用于输入输出。
<iomanip> 该文件通过所谓的参数化的流操纵器(比如 setwsetfillsetprecision),来声明对执行标准化 I/O 有用的服务。
<fstream> 该文件定义了 ifstreamofstreamfstream 对象,用于文件读写。

<iostream>

下图摘录了 iostream 类的继承关系及成员函数,如下:

iostream classes

http://www.cplusplus.com/doc/tutorial/basic_io/

http://www.cplusplus.com/reference/iolibrary/

output stream

输出流与流插入运算符 << 配合使用。<iostream> 提供了下列三种输出流对象:

  • cout 标准输出流(默认设备是显示器屏幕)
  • cerr 无缓冲标准错误输出流(默认设备是显示器屏幕)
  • clog 有缓冲标准错误输出流(默认设备是打印机)

此外,<iostream> 还提供了一个常用的操纵符:

  • endl Insert newline and flush

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 定义一些头文件,这些头文件包含了程序中必需的或有用的信息。
#include <iostream>
// 告诉编译器使用 std 命名空间。命名空间是 C++ 中一个相对新的概念。使用该命名空间之后,std::cout 可以简写为:cout
using namespace std;

// 主函数,程序从这里开始执行。
int main()
{
// 在屏幕上输出 "Hello World"
cout << "hello world" << std::endl;

// 终止 main() 函数,并向调用进程返回值 0。
return 0;
}

// 使用 g++ 编译器,编译 cpp 源文件为可执行文件,并执行之
// cd "/Users/wuqd/Documents/workspace/cpp/" && g++ HelloWorld.cpp -o HelloWorld && "/Users/wuqd/Documents/workspace/cpp/"HelloWorld

运行结果:hello world

input stream

输入流与流提取运算符 >> 配合使用。<iostream> 提供了下列一种输入流对象:

  • cin 标准输入流(默认设备是键盘)

例子:

1
2
3
4
5
cin >> name;
cin >> age;

// 流提取运算符 >> 在一个语句中可以多次使用,如果要求输入多个数据,可以使用如下语句:
cin >> name >> age;

<iomanip> 流操纵器

常用流操纵器函数如下:

函数 作用
setw Set field width
setfill Set fill character
setprecision Set decimal precision

setw 函数用于设置字段的宽度,只对紧接着的输出产生作用。当后面紧跟着的输出字段长度小于 n 的时候,在该字段前面默认用空格补齐,当输出字段长度大于 n 时,全部整体输出。如下:

setw

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <iomanip>
using namespace std;

/*
* 测试 I/O
*/
int main()
{
double pi = 3.1415926;

// 默认右补位,可以使用 setf() 进行左补位
cout.setf(ios::left);

// setprecision() 包含小数点
cout << setfill('*') << setw(5) << setprecision(3) << pi << endl;

return 0;
}

运行结果:3.14*

参考:

https://www.runoob.com/w3cnote/cpp-func-setw.html

<fstream> 文件读写

<fstream> 定义了下面三个对象,用于文件读写:

数据类型 描述
ofstream 该数据类型表示输出文件流,用于创建文件并向文件写入信息。
ifstream 该数据类型表示输入文件流,用于从文件读取信息。
fstream 该数据类型通常表示文件流,且同时具有 ofstream 和 ifstream 两种功能,这意味着它可以创建文件,向文件写入信息,从文件读取信息。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
#include <fstream>
using namespace std;

void output() {
ofstream myfile;
// 打开文件
myfile.open("/Users/wuqd/Desktop/test", ios::app); // ios::app 表示追加模式。所有写入都追加到文件末尾。
// 写入文件
myfile << "helloworld" << endl;
// 关闭文件
myfile.close();
}

void input() {
ifstream in;
// 打开文件
in.open("/Users/wuqd/Desktop/test");
char data[100];
// 读取文件
while (in >> data) {
// 输出到屏幕
cout << data << endl;
}
// 关闭文件
in.close();
}

int main() {
output();
input();
return 0;
}

参考:

https://www.cplusplus.com/doc/oldtutorial/files/

https://www.runoob.com/cplusplus/cpp-files-streams.html

继承

C++ 继承的语法如下,支持单继承和多继承:

1
2
3
4
5
// Single inheritance 单继承
class derived_class: access_specifier base_class

// Multiple inheritance 多重继承,各个基类之间用逗号分隔
class derived_class: access_specifier base_class_1, access_specifier base_class_2, ...

继承类型

继承类型通过访问修饰符 access_specifier 来指定:

继承类型 基类的 public 成员 基类的 protected 成员 基类的 private 成员
公有继承(public 派生类的 public 成员 派生类的 protected 成员 无法继承
保护继承(protected 派生类的 protected 成员 派生类的 protected 成员 无法继承
私有继承(private 派生类的 private 成员 派生类的 private 成员 无法继承

通常使用 public 继承,几乎不使用 protectedprivate 继承。

In principle, a publicly derived class inherits access to every member of a base class except:

  • its constructors and its destructor
  • its assignment operator members (operator=)
  • its friends
  • its private members

继承的访问控制属性

Access public protected private
members of the same class yes yes yes
members of derived class yes yes no
not members yes no no

构造、析构函数执行顺序

Even though access to the constructors and destructor of the base class is not inherited, they are automatically called by the constructors and destructor of the derived class.

Unless otherwise specified, the constructors of a derived class calls the default constructor of its base classes (i.e., the constructor taking no arguments).

继承后,执行顺序如下:

  • 构造函数:先父后子
  • 析构函数:先子后父

子类调用父类方法

BaseClass::Function()

多态

One of the key features of class inheritance is that a pointer to a derived class is type-compatible with a pointer to its base class. Polymorphism is the art of taking advantage of this simple but powerful and versatile feature.

类继承的关键特性之一,就是指向派生类的指针与指向其基类的指针在类型上兼容。 多态是利用这一简单但强大而通用的功能的艺术。

下面使用指针来演示多态这一特性。

UML 类图如下:

UML 类图

类声明如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// pointers to base class
#include <iostream>
using namespace std;

class Shape {
protected:
int width, height;
public:
Shape(int width, int height) {
this->width = width;
this->height = height;
}
virtual int area() { return 0; }
};

class Rectangle : public Shape {
public:
Rectangle(int x, int y) : Shape(x, y) {};
int area() { return width * height; }
};

class Triangle : public Shape {
public:
Triangle(int x, int y) : Shape(x, y) {};
int area() { return width * height / 2; }
};

使用方式一:

1
2
3
4
5
6
7
8
9
10
11
12
int main() {
Rectangle rect(2, 3);
Triangle trgl(2, 3);

Shape * p1 = &rect;
Shape * p2 = &trgl;

cout << "rectangle area is " << p1->area() << endl; // rectangle area is 6
cout << "triangle area is " << p2->area() << endl; // triangle area is 3

return 0;
}

使用方式二,参考:Dynamic memory

1
2
3
4
5
6
7
8
9
10
11
12
13
int main() {

Shape * p1 = new Rectangle(2, 3);
Shape * p2 = new Triangle(2, 3);

cout << "rectangle area is " << p1->area() << endl; // rectangle area is 6
cout << "triangle area is " << p2->area() << endl; // triangle area is 3

delete p3;
delete p4;

return 0;
}

描述如下:

Function main declares two pointers to Shape (named p1 and p2). These are assigned the addresses of rect and trgl, respectively, which are objects of type Rectangle and Triangle. Such assignments are valid, since both Rectangle and Triangle are classes derived from Shape.

Dereferencing p1 and p2 (with p1-> and p2->) is valid and allows us to access the members of their pointed objects. For example, the following two statements would be equivalent in the previous example:

1
2
rect.area();
p1->area();

虚函数(virtual)

虚函数是在基类中使用 virtual 关键字声明的函数,是可以在派生类中重定义的成员函数。虚函数用于实现运行时多态

1
2
3
4
5
6
7
8
9
10
11
12
13
+--------------------+
| Base Class |
| virtual function |
+---------^----------+
|
|class inheritance
|
+---------+----------+
| Derived class |
| redefined function|
+--------------------+

运行时多态的实现手段:虚函数 + 继承 + 函数重定义

派生类也可以重定义基类的非虚函数,但无法通过基类的引用来访问派生类的该函数。即:如果移除上述基类中 area 函数声明的 virtual 关键字,下面的函数调用将返回 0,因为实际调用的是基类的版本:

1
2
cout << "rectangle area is " << p1->area() << endl;  // rectangle area is 0
cout << "triangle area is " << p2->area() << endl; // triangle area is 0

Therefore, essentially, what the virtual keyword does is to allow a member of a derived class with the same name as one in the base class to be appropriately called from a pointer, and more precisely when the type of the pointer is a pointer to the base class that is pointing to an object of the derived class, as in the above example.

A class that declares or inherits a virtual function is called a polymorphic class.

注意,尽管成员之一是 virtual 的,但 Sharp 仍然是一个常规类,可以实例化对象。

纯虚函数(抽象类)

Classes that contain at least one pure virtual function are known as abstract base classes. The syntax of pure virtual function is to replace their definition by =0 (an equal sign and a zero):

1
2
3
4
5
6
7
// abstract class CPolygon
class Shape {
protected:
int width, height;
public:
virtual int area () = 0;
};

Abstract base classes cannot be used to instantiate objects:

1
2
3
// 不允许使用抽象类类型 "Shape" 的对象: -- 函数 "Shape::area" 是纯虚函数
// variable type 'Shape' is an abstract class
Shape shape;

But an abstract base class is not totally useless. It can be used to create pointers to it, and take advantage of all its polymorphic abilities.

1
2
3
// the following pointer declarations would be valid
Shape * p1 = &rect;
Shape * p2 = &trgl;

Virtual members and abstract classes grant C++ polymorphic characteristics, most useful for object-oriented projects.

C++ 接口是使用抽象类来实现的。

参考

http://www.cplusplus.com/doc/tutorial/inheritance/

http://www.cplusplus.com/doc/tutorial/polymorphism/

类的声明:

1
2
3
4
5
6
7
class class_name {
access_specifier_1:
member1;
access_specifier_2:
member2;
...
} object_names;

访问修饰符

  • private members of a class are accessible only from within other members of the same class (or from their friend). By default, all members of a class have private access for all its members.
  • protected members are accessible from other members of the same class (or from their friend), but also from members of their derived classes.
  • Finally, public members are accessible from anywhere where the object is visible.

this 指针

在 C++ 中,每一个对象都能通过 this 指针来访问自己的地址。this 指针是所有成员函数的隐含参数。因此,在成员函数内部,它可以用来指向调用对象。

友元函数没有 this 指针,因为友元不是类的成员。只有成员函数才有 this 指针。

静态成员(static)

static 关键字用于修饰静态成员变量或函数。限制如下:

  • 无法访问类的非静态成员变量或函数;
  • 无法使用 this 指针。

静态成员的引用方式:Runoob:runoob_age

成员函数

有两种方式定义类的成员函数:

  • 内联成员函数(inline member function)
  • 普通成员函数(not-inline member function)

两种方式并不会导致行为上的差异,而只会导致可能的编译器优化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// classes example
#include <iostream>
using namespace std;

class Rectangle {
int width, height;
public:
// declaration of a member function within the class
void set_values (int, int);
// defining a member function completely within the class definition
int area() {return width*height;}
};

// definition of a member function of a class outside the class itself.
// The scope operator (::) specifies the class to which the member being defined belongs, granting exactly the same scope properties as if this function definition was directly included within the class definition.
void Rectangle::set_values (int x, int y) {
width = x;
height = y;
};

int main () {
Rectangle rect;

// public members of object can be accessed by dot operator (.)
rect.set_values (3,4);
cout << "area: " << rect.area() << endl;
return 0;
}

常成员函数

To specify that a member is a const member, the const keyword shall follow the function prototype, after the closing parenthesis for its parameters:

1
int get() const {return x;}        // const member function

构造函数

类的构造函数是类的一种特殊的成员函数,构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。它会在每次创建类的新对象时执行:

1
2
3
4
// 调用有参构造函数
Rectangle rect(1, 2); // Object is being created, width=1, height=2
// 调用默认构造函数
Rectangle rectb; // Object is being created

构造函数重载

Overloading constructors

Like any other function, a constructor can also be overloaded with different versions taking different parameters: with a different number of parameters and/or parameters of different types. The compiler will automatically call the one whose parameters match the arguments:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Rectangle {
int width, height;
public:
// 声明一个无参构造函数
Rectangle();
// 声明一个有参构造函数
Rectangle(int, int);
};

// 定义一个无参构造函数
Rectangle::Rectangle() {
cout << "Object is being created" << endl;
}

// 定义一个有参构造函数
Rectangle::Rectangle(int width, int height) {
this->width = width;
this->height = height;
cout << "Object is being created, width=" << this->width << ", height=" << this->height << endl;
};

This example introduces a special kind constructor: the default constructor. The default constructor is the constructor that takes no parameters, and it is special because it is called when an object is declared but is not initialized with any arguments. In the example above, the default constructor is called for rectb. Note how rectb is not even constructed with an empty set of parentheses - in fact, empty parentheses cannot be used to call the default constructor:

1
2
Rectangle rectb;   // ok, default constructor called
Rectangle rectc(); // oops, default constructor NOT called, empty parentheses interpreted as a function declaration

This is because the empty set of parentheses would make of rectc a function declaration instead of an object declaration: It would be a function that takes no arguments and returns a value of type Rectangle.

在构造函数中初始化成员变量

使用构造函数初始化其他成员变量时,有下面两种方式:

Member initialization in constructors

When a constructor is used to initialize other members, these other members can be initialized directly, without resorting to statements in its body. This is done by inserting, before the constructor’s body, a colon (:) and a list of initializations for class members. For example, consider a class with the following declaration:

1
2
3
4
5
6
class Rectangle {
int width, height;
public:
Rectangle(int, int);
int area() {return width*height;}
};

The constructor for this class could be defined, as usual, as:

1
Rectangle::Rectangle(int x, int y) { width=x; height=y; }

But it could also be defined using member initialization as:

1
Rectangle::Rectangle(int x, int y) : width(x), height(y) { }

Or even:

1
Rectangle::Rectangle(int x, int y) : Shape(x, y) { }

Note how in this last case, the constructor does nothing else than initialize its members, hence it has an empty function body.

析构函数(~)

类的析构函数是类的一种特殊的成员函数,它会在每次删除对象时执行,有助于在跳出程序前释放资源(比如关闭文件、释放内存等)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Rectangle {
int width, height;
public:
// 构造函数
Rectangle();
// 析构函数
~Rectangle();
};

Rectangle::Rectangle(){
cout << "Object is being created" << endl;
};

Rectangle::~Rectangle(){
cout << "Object is being deleted" << endl;
};

析构函数要点:

  • 析构函数名称与类的名称完全相同,前缀使用关键字 ~
  • 一个类中只能声明一个析构函数(destructor cannot be redeclared);
  • 析构函数无参数(destructor cannot have any parameters);
  • 析构函数无返回值(destructor cannot have a return type);
  • 不可重载。

友元函数(friend)

类的友元函数(friend 关键字),有权访问类的所有私有(private)和保护(protected)成员变量。尽管友元函数在类中声明,但是友元函数并不是类的成员函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Rectangle {
int width, height;
public:
friend int getWidth(Rectangle);
friend int getHeight(Rectangle rect) {
return rect.height;
}
};

int getWidth(Rectangle rect) {
return rect.width;
}

int main () {
Rectangle rect1(1, 2);

cout << "width: " << getWidth(rect1) << " height: " << getHeight(rect1) << endl; // width: 1 height: 2
return 0;
}

友元函数破坏了类的封装性,实践中不建议使用。

重载(overload)

函数重载

可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。

运算符重载

一、支持重载的运算符:

C++ allows most operators to be overloaded so that their behavior can be defined for just about any type, including classes. Here is a list of all the operators that can be overloaded:

1
2
3
4
+    -    *    /    =    <    >    +=   -=   *=   /=   <<   >>
<<= >>= == != <= >= ++ -- % & ^ ! |
~ &= ^= |= && || %= [] () , ->* -> new
delete new[] delete[]

Operators are overloaded by means of operator functions, which are regular functions with special names: their name begins by the operator keyword followed by the operator sign that is overloaded. The syntax is:

1
type operator sign (parameters) { /*... body ...*/ }

二、重载运算符的不同形式:

There is a table with a summary of the parameters needed for each of the different operators than can be overloaded (please, replace @ by the operator in each case):

Expression Operator Member function Non-member function
@a + - * & ! ~ ++ -- A::operator@() operator@(A)
a@ ++ -- A::operator@(int) operator@(A,int)
a@b + - * / % ^ & | < > == != <= >= << >> && || , A::operator@(B) operator@(A,B)
a@b = += -= *= /= %= ^= &= |= <<= >>= [] A::operator@(B) -
a(b,c...) () A::operator()(B,C...) -
a->b -> A::operator->() -
(TYPE) a TYPE A::operator TYPE() -

Where a is an object of class A, b is an object of class B and c is an object of class C. TYPE is just any type (that operators overloads the conversion to type TYPE).

Notice that some operators may be overloaded in two forms: either as a member function or as a non-member function.

三、例子:

For example, cartesian vectors are sets of two coordinates: x and y. The addition operation of two cartesian vectors is defined as the addition both x coordinates together, and both y coordinates together. For example, adding the cartesian vectors (3,1) and (1,2) together would result in (3+1,1+2) = (4,3). This could be implemented in C++ with the following code:

Overloading operators

The function operator+ of class CVector overloads the addition operator (+) for that type. Once declared, this function can be called either implicitly using the operator, or explicitly using its functional name:

1
2
3
4
5
// called either implicitly using the operator
c = a + b;

// or explicitly using its functional name
c = a.operator+ (b);

Both expressions are equivalent.

四、注意点:

Attention

The operator overloads are just regular functions which can have any behavior; there is actually no requirement that the operation performed by that overload bears a relation to the mathematical or usual meaning of the operator, although it is strongly recommended. For example, a class that overloads operator+ to actually subtract or that overloads operator== to fill the object with zeros, is perfectly valid, although using such a class could be challenging.

函数重定义(redefine)

即 Java 语言中的方法重写(rewrite)。

类指针

Objects can also be pointed to by pointers: Once declared, a class becomes a valid type, so it can be used as the type pointed to by a pointer. For example:

1
2
// a pointer to an object of class Rectangle.
Rectangle * prect;

Similarly as with plain data structures, the members of an object can be accessed directly from a pointer by using the arrow operator (->). Here is an example with some possible combinations:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// pointer to classes example
#include <iostream>
using namespace std;

class Rectangle {
int width, height;
public:
Rectangle(int x, int y) : width(x), height(y) {}
int area(void) { return width * height; }
};

int main() {
Rectangle obj(3, 4);
Rectangle * foo, * bar; // 类指针(Pointers to classes)
foo = &obj;
bar = new Rectangle (5, 6); // 参考:http://www.cplusplus.com/doc/tutorial/dynamic/
cout << "obj's area: " << obj.area() << '\n';
cout << "*foo's area: " << foo->area() << '\n';
cout << "*foo's area: " << (*foo).area() << '\n';
cout << "*bar's area: " << bar->area() << '\n';
cout << "*bar's area: " << (*bar).area() << '\n';
delete bar;
return 0;
}

This example makes use of several operators to operate on objects and pointers (operators *, &, ., ->). They can be interpreted as:

expression can be read as
*x pointed to by x
&x address of x
x.y member y of object x
x->y member y of object pointed to by x
(*x).y member y of object pointed to by x (equivalent to the previous one)

模板

模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。

函数模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// function templates
#include <iostream>
using namespace std;

template <typename T>
T getmax (T a, T b)
{
T retval;
retval = a > b ? a : b;
return retval;
}

int main () {
int maxInt = getmax(100, 75);
cout << maxInt << endl; // 100

double maxDouble = getmax(3.3, 2.18);
cout << maxDouble << endl; // 3.3

// no matching function for call to 'getmax'
// char maxChar = getmax('a', 1.99);
// cout << maxChar << endl;

return 0;
}

类模板

Just like we can create function templates, we can also create class templates, allowing classes to have members that use template parameters as types. For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// class templates
#include <iostream>
using namespace std;

template <class T>
class MyPair {
T a, b;
public:
MyPair (T first, T second)
{a=first; b=second;}
T getmax ();
};

// In case that a member function is defined outside the defintion of the class template, it shall be preceded with the template <...> prefix
template <class T>
T MyPair<T>::getmax ()
{
T retval;
retval = a > b ? a : b;
return retval;
}

int main () {
MyPair<int> myobject(100, 75);
cout << myobject.getmax() << endl; // 100

MyPair<double> myfloats(3.3, 2.18);
cout << myfloats.getmax() << endl; // 3.3

// implicit conversion from 'double' to 'char' changes value from 1.99 to 1
// MyPair<char> mychars('a', 1.99);
// cout << mychars.getmax() << endl;

return 0;
}

Notice the syntax of the definition of member function getmax:

1
2
template <class T>
T mypair<T>::getmax ()

There are three T‘s in this declaration: The first one is the template parameter. The second T refers to the type returned by the function. And the third T (the one between angle brackets) is also a requirement: It specifies that this function’s template parameter is also the class template parameter.

模板类

模板类是类模板实例化后的一个产物。

It is possible to define a different implementation for a template when a specific type is passed as template argument. This is called a template specialization.

For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// template specialization
#include <iostream>
using namespace std;

// class template:
template <class T>
class mycontainer {
T element;
public:
mycontainer (T arg) {element=arg;}
T increase () {return ++element;}
};

// class template specialization:
template <>
class mycontainer <char> {
char element;
public:
mycontainer (char arg) {element=arg;}
char uppercase ()
{
if ((element>='a')&&(element<='z'))
element+='A'-'a';
return element;
}
};

int main () {
mycontainer<int> myint (7);
mycontainer<char> mychar ('j');
cout << myint.increase() << endl; // 8
cout << mychar.uppercase() << endl; // J
return 0;
}

This is the syntax used for the class template specialization:

1
2
template <>
class mycontainer <char> { ... };

First of all, notice that we precede the class name with template<> , including an empty parameter list. This is because all types are known and no template arguments are required for this specialization, but still, it is the specialization of a class template, and thus it requires to be noted as such.

But more important than this prefix, is the <char> specialization parameter after the class template name. This specialization parameter itself identifies the type for which the template class is being specialized (char). Notice the differences between the generic class template and the specialization:

1
2
template <class T> class mycontainer { ... };
template <> class mycontainer <char> { ... };

The first line is the generic template, and the second one is the specialization.

When we declare specializations for a template class, we must also define all its members, even those identical to the generic template class, because there is no “inheritance” of members from the generic template to the specialization.

参考

http://www.cplusplus.com/doc/tutorial/classes/

http://www.cplusplus.com/doc/tutorial/templates/

http://www.cplusplus.com/doc/tutorial/classes2/

https://www.cplusplus.com/doc/oldtutorial/templates/

https://www.runoob.com/cplusplus/cpp-classes-objects.html

https://www.runoob.com/cplusplus/cpp-templates.html

A type alias is a different name by which a type can be identified. In C++, any valid type can be aliased so that it can be referred to with a different identifier.

在 C++ 中,有两种创建类型别名的语法:

  1. 从 C 语言继承而来,使用 typedef 关键字:

    1
    typedef existing_type new_type_name ;
  2. 由 C++ 语言引入,使用 using 关键字:

    1
    using new_type_name = existing_type ;

existing_type 可以是任何类型,无论是基本类型还是复合类型:

例子一:

typedef using
typedef char C; using C = char;
typedef unsigned int WORD; using WORD = unsigned int;
typedef char * pChar; using pChar = char *;
typedef char field [50]; using field = char [50];

例子二,下面两种定义结构体类型的方式是等价的:

1
2
3
4
5
6
7
8
9
struct product {
int weight;
double price;
};

typedef struct {
int weight;
double price;
} product;

new_type_name 作为该类型的别名,用法如下:

1
2
3
4
C mychar, anotherchar, *ptc1;
WORD myword;
pChar ptc2;
field name;

一旦定义了这些别名,就可以像其它有效类型一样,在任何声明中使用。尤其常见于与结构体搭配使用。

参考

https://www.cplusplus.com/doc/tutorial/other_data_types/

声明语法

1
2
3
4
5
6
7
struct type_name {
member_type1 member_name1;
member_type2 member_name2;
member_type3 member_name3;
.
.
} object_names;

定义用法

结构体对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// declare three objects (variables) of this type (product): apple, banana, and melon.
struct product {
int weight;
double price;
} apple, banana, melon;

// struct requires either a type_name or at least one name in object_names, but not necessarily both.
struct {
int weight;
double price;
} apple, banana, melon;

// declare three objects (variables) of this type (product): apple, banana, and melon.
product apple, banana, melon;

结构体数组

1
2
// because structures are types, they can also be used as the type of arrays.
product banana[3];

结构体指针

1
product * p = &apple;

创建结构体指针之后,可以使用以下运算符访问其成员变量:

Operator Expression What is evaluated Equivalent
dot operator (.) a.b Member b of object a
arrow operator (->)
(dereference operator)
a->b Member b of object pointed to by a (*a).b

例子:

1
2
3
4
5
apple.weight;  // 3
// The arrow operator (->) is a dereference operator that is used exclusively with pointers to objects that have members. This operator serves to access the member of an object directly from its address.
p->weight; // 3
// equivalent to:
(*p).weight; // 3

例子 2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
using namespace std;

struct product {
int weight;
double price;
};

void test() {
product apple;
apple.weight = 3;
apple.price = 4;
product * p_apple = &apple;

cout << "weight: " << (*p_apple).weight << endl; // weight: 3
cout << "weight: " << p_apple->weight << endl; // weight: 3

cout << "address of apple: " << p_apple << endl; // address of apple: 0x7ffee256b5b0
cout << "address of weight: " << &p_apple->weight << endl; // address of weight: 0x7ffee256b5b0
cout << "address of price: " << &p_apple->price << endl; // address of price: 0x7ffee256b5b8
cout << "size of weight: " << sizeof(p_apple->weight) << endl; // size of weight: 4
cout << "size of price: " << sizeof(p_apple->price) << endl; // size of price: 8
}

int main() {
test();
return 0;
}

结构体作为函数参数

支持三种方式的传参:

  • 传值
  • 传引用
  • 传址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
using namespace std;

struct product {
int weight;
double price;
} lemon;

// 传值
void show(product prd) {
cout << "weight: " << prd.weight << " price: " << prd.price << endl;
}

// 传引用
void show2(product &prd) {
cout << "weight: " << prd.weight << " price: " << prd.price << endl;
}

// 传址
void show3(product * prd) {
cout << "weight: " << prd->weight << " price: " << prd->price << endl;
}

void test() {
lemon.weight = 1;
lemon.price = 2;
show(lemon); // weight: 1 price: 2
show2(lemon); // weight: 1 price: 2
show3(&lemon); // weight: 1 price: 2
}

int main() {
test();
return 0;
}

参考

http://www.cplusplus.com/doc/tutorial/structures/

重点:区分下述四种形式:

1
2
3
4
5
6
7
// 字符数组(Character sequences)
char str[] = {'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '\0'};
char str[] = "hello world";
// 字符串指针
char *str = "hello world";
// 字符串类(string)
string str = "hello world";

字符数组

字符数组(Character sequences):http://www.cplusplus.com/doc/tutorial/ntcs/

字符串实际上是使用 null 字符 \0 终止的一维字符数组,如下:

C 字符串

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 字符串实际上是使用 null 字符 \0 终止的一维字符数组
char str[] = {'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '\0'};
// 依据数组初始化规则,简化如下:
char str2[] = "hello world";
// conversion from string literal to 'char *' is deprecated
char *str3 = "hello world";

// str: hello world size: 12
cout << "str: " << str << " size: " << sizeof(str) << endl;
// str2: hello world size: 12
cout << "str2: " << str2 << " size: " << sizeof(str2) << endl;
// 'sizeof (str3)' will return the size of the pointer, not the array itself
// str3: hello world size: 8
cout << "str3: " << str3 << " size: " << sizeof(str3) << endl;

字符串指针

用字符数组和字符串指针都可实现字符串的存储和运算,但是两者是有区别的:

  • 字符数组是一个数组,每个元素的值都可以改变。
  • 而字符串指针指向的是一个常量字符串,它被存放在程序的静态数据区,一旦定义就不能改变

这是最重要的区别。下面的代码在运行期间将会出错:

1
2
str2[1] = 'a';      // hallo world
*(str3 + 1) = 'a'; // 运行时出错。因为不能改变字符串常量的值

string 字符串类

string 头文件提供了 string 类,参考:http://www.cplusplus.com/reference/string/

cstring 操纵器

cstring 头文件提供了大量的函数,用来操纵 C strings and arrays,参考:http://www.cplusplus.com/reference/cstring/

例如:

  • Copying:
  • Concatenation:
  • Comparison:
  • Searching:
    • strchr Locate first occurrence of character in string
    • strrchr Locate last occurrence of character in string
  • Other:

参考

https://www.cplusplus.com/doc/tutorial/ntcs/

https://www.runoob.com/cplusplus/cpp-strings.html

C++输出char型变量与字符串的地址

https://stackoverflow.com/questions/1524356/c-deprecated-conversion-from-string-constant-to-char