自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(24)
  • 收藏
  • 关注

原创 关于我自己

我从2015年开始接触编程,2016年高考进入北京某211大学的计算机专业,2020年毕业,到今天是第7个年头。7年的时间,说来也不算短了;但惭愧的是,这其间连学习带工作,真正在编程的时间并不算长。这里是我的私人博客,按理来讲不会有别人看,所以很多心里话也可以说出来了。坦率地讲,在专业方面,我一直是一个不太自信的人:论编程能力,我在校期间打过蓝桥杯,但是在第二轮就被淘汰了;论学习能力,我曾经花费了整整一天的时间,都没能弄懂"九九乘法表"到底是怎么输出的;论设计能力,我在公司里连续两届评选被认为是技

2021-08-29 02:10:13 853

原创 顺序表(线性表的顺序存储实现)

一、顺序表的设计思路顺序表是采取顺序存储方式的一种线性表实现。所谓的“顺序存储方式”,就是指采用一块连续的内存来保存数据。这样一来,顺序表中元素的邻接关系就可以直接由内存的连续性来保证;简而言之,逻辑上相邻的元素,在其物理地址上也是相邻的。为了保证表中元素的逻辑关系,顺序表的实现必须保证表中的元素在这块连续的内存中依次排布,并且中间不能存在空缺的内存单元,因为空缺的内存会导致表中元素在逻辑上不连续。这样一来,只要知道了顺序表中某个元素在表中的下标位置以及其内存地址,就可以获取顺序表中的每个元素。更具体

2021-01-31 19:51:16 2207

原创 线性表(理论)

一、线性表的定义和特点线性表是具有相同特性的数据元素的一个有限序列:L=(a1,a2,...,ai−1,ai,ai+1,...,an)L = (a_1, a_2, ..., a_{i-1}, a_i, a_{i+1}, ..., a_n)L=(a1​,a2​,...,ai−1​,ai​,ai+1​,...,an​)要注意的问题是:线性表是一个由数据元素组成的序列。线性表中元素的数据类型是相同的,这意味着每个元素在内存中所占用的空间是相同的。线性表中的元素是有限的。在一个线性表中,a1是

2021-01-30 00:39:12 318

原创 数据结构与算法概述

一、数据结构概述1. 概念数据是能够输入计算机且能够被计算机处理的各种符号的集合,它既包括数值类型的数据(例如整数、浮点数等),又包括非数值型的数据(文字、图像、声音)等。数据元素是数据的基本单位,在计算机程序中通常作为一个整体进行考虑和处理(类似于高级语言中的对象)。数据项是构成数据元素的不可分割的最小单位(类似于高级语言定义的类中的数据成员)。数据对象是性质相同的数据元素的集合,是数据的一个子集。数据结构是数据元素相互之间的关系,是相互之间存在一种或多种特定关系的数据元素

2021-01-29 23:37:38 177

原创 《Effective C++》Item19:设计类之前需要深思熟虑

本条款在原书中的名称是:设计类犹如设计类型。C++就像其他支持面向对象的语言一样。当我们定义了一个新的类,也就定义了一个新的类型。身为C++程序员,我们的许多时间主要用来扩张我们自己的类型系统。这意味着我们并不只是类的设计者,还是类型的设计者。重载函数和操作符、控制内存的分配和回收、定义对象的初始化和析构等等,这些操作全部交给我们来控制。因此,我们应该带着和“语言设计者当初设计语言内置类型时”一样的谨慎来研讨类的设计。设计优秀的类库是一项艰巨的工作,因为设计好的类型是一项艰巨的工作。好的类型有自然的.

2020-07-11 16:05:13 131

原创 《Effective C++》Item18:让接口容易被正确使用,不易被误用

C++中充满了接口:function接口、class接口、template接口等,每一种接口都是客户与我们的代码互动的手段。假设我们面对的是一群“讲道理”的人,这部分客户希望能够正确地使用我们提供的借口。在这种情况下,如果它们对其中的任何一个接口的用法不正确,那么我们作为接口的设计者,也应该承担一些连带的责任。因为理论上,如果客户企图使用某个接口,但是却没有实现他所预期的行为,那么这段代码就不应该通过编译;反过来说,只要代码通过了编译,那么程序的行为就应该是用户所期望的那样。想要开发一个“容易被正确使用、

2020-07-10 15:07:22 205

原创 《Effective C++》Item17:使用独立的语句将底层资源放入RTTI对象中

假设现在我们有一个函数用来处理程序的优先级,另一个函数用来在某个动态分配所得的对象上进行一些带有优先级的处理:int priority();void processObject(std::shared_ptr<Object> obj, int priority);由于我们时刻牢记“使用对象管理资源”的智慧铭言,因此决定对动态分配获得的对象使用智能指针。现在考虑如何来调用processObject函数:processObject(std::shared_ptr<Object&gt

2020-07-09 18:26:27 167 1

原创 《Effective C++》Item16:成对使用new/delete和new[]/delete[]

以下代码有什么问题?std::string *stringArray = new std::string[100];//...delete stringArray;每件事情看起来都井然有序。使用了new,也搭配了对应的delete。但还是有一点完全错误:程序的行为是未定义的。在最好的情况下,stringArray所包含的100个string对象中的99个不太可能被正确地回收,因为它们的析构函数很可能没有被调用。当使用了new的时候,有两件事情发生:调用operator new分配内存。对象

2020-07-09 17:56:34 189

原创 《Effective C++》Item15:在资源管理类中提供对原始资源的访问

资源管理类很棒,它们是我们对抗资源泄露的堡垒,可以完全依赖它们回收底层资源。然而,当代码需要向后兼容,或者引入第三方库的时候,仍然会遇到麻烦:许多现有的API需要直接提供底层资源。举个例子,条款13曾经引入了一个例子,使用智能指针auto_ptr来保存工厂函数createInvestment返回的指针:std::auto_ptr<Investment> invest(createInvestment());假设现在引入了一个旧的API,或者是第三方库:int daysHeld(cons

2020-07-09 16:03:45 183

原创 《Effective C++》Item14:在资源管理类中当心拷贝操作

条款13中引入了这样的概念:资源取得的时机便是初始化资源管理对象的时机。然而,资源的种类是很多的,我们总是需要编写自己的资源管理类。例如,假设我们使用C风格的API来处理类型为Mutex的互斥量对象,提供了两种操作:锁定操作lock,以及解锁操作unlock。void lock(Mutex *mutex);void unlock(Mutex *mutex);为了确保绝对不会忘记将一个被锁定的互斥量解锁,我们需要建立一个资源管理类:class MutexGuard{public: ex

2020-07-09 15:19:00 129

原创 《Effective C++》Item13:使用RAII对象管理资源

所谓的“资源”,指的就是那些“一旦使用,将来必须要归还给操作系统”的组件。在C++程序当中,最常使用的资源就是堆内存;然而内存只是我们必须管理的众多资源之一,其它常用的资源还包括:文件描述符、线程互斥量、网络连接等等。无论是哪一种资源,在使用完毕后都必须要归还给服务提供者。尝试在任何情况下都手工维护资源,实际是一件很困难的事。因为一旦引入了多种执行流、程序异常等特性,那么手中的资源就很容易失去控制。假使我们使用一个用来抽象投资行为的类库,其中各种各样的投资类型都继承自同一个基类Investment:c

2020-07-09 14:34:36 241

原创 《Effective C++》Item12:复制对象时切勿忘记每一个成分

设计良好的面向对象软件系统会将对象的内部封装起来,只留两个函数负责对象的拷贝,那便是拷贝构造函数和拷贝赋值运算符。在条款5中,我们已经指出编译器会在必要的时候为我们的类生成这两个函数,在其中对每个成员变量进行浅拷贝。一旦我们声明了自己的拷贝构造函数或者拷贝赋值运算符,编译器不仅不再负责对应函数的生成,甚至连检查都完全不进行。这在某些时候将会导致问题。例如,我们写了一个类Customer来表示顾客,并且手工书写了拷贝构造函数和拷贝赋值运算符,用于打印日志:class Customer{public:

2020-07-07 19:25:51 124

原创 《Effective C++》Item11:在operator=中处理自我赋值

所谓的“自我赋值”发生在对象被赋值给自己时:class Object { /* .. */ }int main(){ Object o; o = o; //自我赋值。 return 0;}这种写法看上去没有任何意义,但是它是可以通过编译的;最关键的是,这种操作往往不会显示地发生,例如:int main(){ Object o; //... Object *p1 = &o; Object *p2 = &o; //

2020-07-07 18:25:49 141

原创 《Effective C++》Item10:让所有与赋值相关的运算符返回指向当前对象的引用

在C++中,赋值可以是递归的:int x, y, z;x = y = z = 10;并且,赋值是遵循右结合律的,所以上面的赋值语句等价于:x = (y = (z = 10));所以,为了实现这样的连锁赋值方式,类的拷贝复制运算符必须要返回一个指向当前对象的引用:class Object{ //...public: Object &operator=(const Object &other) { //... retu

2020-07-07 16:41:40 205

原创 《Effective C++》Item9:不要在构造和析构函数中调用虚函数

这是一条在C++中非常重要的条款,也是C++和其他更高级的语言(例如Java、C#等)不同的地方。假设现在我们编写了一些类,用于抽象股市中买卖股票的订单:class Transaction{public: Transaction() { //... this->logTransaction(); } virtual void logTransaction() const = 0;};class BuyTransactio

2020-07-07 15:53:22 171

原创 《Effective C++》Item8:不要让析构函数抛出异常

从语法层面来看,C++是允许在析构函数中抛出异常的,但是并不鼓励我们这样做。因为在一些特殊情况下,析构函数抛出的异常将直接引发程序的崩溃。看一个例子:书中提供了一个示例程序,但我认为不足以说明问题。因此我提供了另外一个更明显的例子。class Object{public: ~Object() { throw "There is an exception"; }};int main(){ try { Object o; // ... throw "There is

2020-07-07 14:55:07 984

原创 《Effective C++》Item7:为多态基类声明虚析构函数

多态的特点是:调用相同的接口,程序的运行时行为取决于动态绑定在这个指针或者引用上的实现。例如,为了抽象出获取时间的方法,可以设计一个基类用于规定一个统一的接口,而不同的派生类用于实现各自的计时方式:class Timer{public: Timer(); ~Timer(); //...};class UTCTimer : public Timer{public: //...};class AtomicTimer : public Timer{pub

2020-07-06 17:19:31 133

原创 《Effective C++》Item6:如果不想使用编译器提供的默认函数,就该明确拒绝

对于某些单例而言,其对象应是不允许被拷贝的。然而,编译器却会默认为我们生成拷贝构造函数和拷贝赋值运算符。如何才能关闭这一特性呢?答案的关键是,所有编译器的产出函数都是public。为了阻止这些函数,我们要得自行声明它们。因此,我们可以将它们声明为private。这样,编译器就不再有理由为我们合成默认的函数了:因为我们已经自己提供了它们的声明。另一个关键的技巧是声明它们,却不提供实现。因为成员函数和友元函数当中可能会利用实现来复制出多份对象的副本。这样一来,如果客户的代码尝试拷贝单例对象,则会导致链接过程

2020-07-06 16:20:44 1286

原创 《Effective C++》Item5:了解C++默默编写并调用了哪些函数

如果一个C++类没有声明自己的构造函数、拷贝构造函数、拷贝赋值运算符和析构函数的话,那么编译器就会为我们生成一个。所有这些由编译器为我们生成的函数都是public且inline的。这些函数只有当被调用到的时候,才会被创建出来。但是这些函数都做了什么呢?主要是处理一些程序幕后的工作,即:编译器向这些函数中安插了额外的代码来处理一些常规事务,例如:调用父类的构造函数和析构函数、初始化虚函数表指针等。至于拷贝构造函数和拷贝赋值运算符,编译器只是简单地将对象中的成员变量进行浅拷贝。例如下面的这个类:temp

2020-07-06 15:36:08 166

原创 《Effective C++》Item4:确保对象在使用之前已经被初始化

在C++中,对象的定义和初始化是两码事。普通对象的初始化例如,如果写出这样的代码:int x;编译器将仅仅为变量x在栈上分配一块内存空间,但是不会向其中写入初始值。对象的定义指的是为其分配内存的过程,往往由全局的operator new函数完成;对象的初始化指的是向这块内存中写入恰当的值,让对象变为可用状态的过程,往往由对象的构造函数完成。这也是体现C++复杂的一个方面。例如,C++完全继承了C语言中数组的特性。因此,仅定义的一个数组是仅定义而未被初始化的;但是STL中的各种容器都实现了自

2020-07-06 12:16:30 244

原创 《Effective C++》Item3:尽量使用const

const的一个奇妙的特性是,他允许你指定一个语义约束,即被const修饰的对象都是不可修改的,而编译器会帮助你在编译时维护这个性质,这能让你更早地发现程序设计过程中的漏洞和缺陷。因此,只要允许,就应该明确地使用const对变量进行限定。const对象只要是对象,那么不管它在哪,都可以添加const修饰;对于指针,还可以分别指定指针本身以及指针所指向的对象是否可以修改:char greeting[] = "hello";char *p1 = greeting;const char *p2 = gr

2020-07-06 11:10:52 1573

原创 《Effective C++》Item2:尽量以const、enum、inline代替#define

宏的问题在于:处理它的是预处理器,而不是编译器。考虑如下的定义:#define PI 3.1415926在预处理器工作时,会将实际代码中的所有PI都直接替换为3.1415926,所以编译器在工作的时候根本看不到PI这个记号。由此产生的问题是:PI没有进入编译器的符号表。于是当你使用PI但是不幸收到一个编译错误时,其错误信息可能包含3.1415926,而不是PI。如果这个宏不是由你定义的,那么这个错误很可能给你造成很大的困扰。解决这个问题的一个方法是使用常量替换掉这个宏:const double P

2020-07-06 11:09:08 140

原创 《Effective C++》Item1:视C++为一个语言联邦

条款1:视C++为一个语言联邦一开始,C++只是C加上一些面向对象特性。C++最初的名称C with class也反映了这一点。但是当这个语言逐渐成熟,它变得更活跃更无拘束,开始接受各种新观念、新特性和新的编程战略。异常(exception)对结构化程序引入了不同的做法,而模板(template)为我们带来了新的思考方式。今天的C++已经是个多重范型编程语言,一个同时支持面向过程、面向对象、模板、模板元编程的语言。这些能力和弹性使得C++已经成为了一个无可匹敌的工具,但也可能会引发某种疑惑:所有的“适

2020-07-06 11:07:00 268

原创 C++从入门到精通——基本数据类型

C++基本数据类型在C++中,基本数据类型包含以下几种:空类型:表示空指针类型,即nullptr_t类型,占用一个指针的大小(平台相关)。布尔类型:表示真或假,即true或是false(占用一个字节)。字符类型:表示单一的字符。根据不同的宽度,又可以细分为以下三种:char:单字节字符集中的一个字符。char16_t:该类型用于表示utf16的一个码点,宽度为两个字节。char32_t:该类型用于表示utf32的一个码点,宽度为四个字节。wchar_t:该类型用于表示适合于编译

2020-06-16 09:17:12 1212

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除