Web Analytics
yangyang

码农兼一个普普通通小青年

C++ C++ is best!!!


C++中的成员指针

C++的有些知识点可能不在《C++ Primer,5th》或者《The C++Programming Language,4th》里,作为一个看这两本书的初学者,这有些难办。C++的有些知识藏在犄角旮旯里,但时不时就能碰到,今天就谈谈遇到的成员指针。跟C#或者Java中成员变量或者函数必须定义在类里面不同,C++中的成员变量或者函数既可以写在类的外面,也可以写在类的里面,这或许是为了兼容C的缘故。针对指向的是否是成员变量或成员函数,C++中必须要进行区分,这或许就是成员指针的由来,本文简单介绍C++成员指针中的成员变量指针和成员函数指针。 …

Pointers to data members Pointers to member functions

C++中的虚函数表以及动态分发

在C++中,如果一个类有虚函数,那么该类的对象在实例化后,会多一个指向虚函数表(virtual table, vtbl)的指针(vitrual table pointer, vptr)。虚函数表及虚函数调用是C++实现多态或者说动态分发(dynamic dispatch)的基础。本文首先介绍一下C++中的对象的虚函数指针以及类的虚函数表的结构,然后介绍了通过使用虚函数表实现动态分发的原理,最后讨论了虚函数调用的性能。 …

static dispatch dynamic dispatch early binding virtual table

使用Windows Subsystem for Linux安装Ubuntu以及配置C++开发环境

随着Windows开始拥抱Linux,在Windows 10以及之后的操作系统里内置了Windows Subsystem For Linux(WSL)模块,可以很方便的在Windows上使用和体验Linux,并且资源开销相对于虚拟机来说要小的多,相当于在Windows上安装了一个名为Ubuntu的应用,并且可以在Windows上直接访问和查看Linux下面的文件系统。我初步体验下来还是非常友好和方便的,Linux操作起来在有些情况下就是比Windows下面更加直接和高效。本文记录一下在Windows 11上启用WSL,安装Ubuntu,以及Ubuntu里面搭建C++的基本开发环境的步骤,算是一个笔记,后面在熟悉Linux操作系统以及学习C++的时候,直接就可以使用WSL里面的Ubunut环境。 …

Ubuntu C++ WSL

告别暴力终止线程

有时候需要让一个线程终止运行,.NET Framework的Thread类中提供了Abort方法,调用某个线程实例对象的Abort方法,可以让该线程类抛出一个ThreadAbortException从而终止该线程的运行。但使用Abort方法终止一个线程对象显得比较暴力,而且可能会有很多潜在的问题,尤其是一个线程调用另外一个线程的Thread.Abort方法终止时。好消息是从.NET 5.0开始Thread.Abort方法就已经被弃用,如果调用就会抛出“PlatformNotSupportedException”,本文主要介绍简单粗暴杀手线程的可能危害,以及如何优雅地退出线程的方法。 …

Thread.Abort cooperative cancellation model CancellationTokenSource

C++内存管理基础

本文也是候捷《C++内存管理机制》的笔记,介绍了C++原始内存分配的基本方法,优缺点,以及介绍了一个简单的内存池,虽然在Modern C++中已经不建议直接管理内存分配后的指针,而是采用智能指针的方式。但是这些只是对原始内存分配的一些包装或处理,所以这里记录一下。 基本方式 《Effective C++》这本书开篇就指出,C++由几大部分组成: C语言部分。C++是以C为基础,C++经过编译器处理后就是C语言。在C++中仍然保留了很多C语言的用法,比如区块(blocks)、语句(statement),数组(arrays)、指针(pointer)等等。所以C语言中的一些内存分配方法,在C++中仍然能够使用。 面向对象,就是C with Class,包括类、封装、继承、多态等等。 模版,Template C++是C++泛型编程的一部分。这一点跟C#中的泛型编程个人感觉区别巨大。但C++中的模版 …

memory management memory pool allocator embedded pointer

C++中的模版模版参数和模版别名的使用

最近在看候捷的C++系列视频教程,其中有一个例子讲到了使用迭代器特性(iterator_traits)和使用模版模版参数(template template parameter)以及模版别名(alias template)来完成同一件事情,非常的经典,我这里记录一下。 事情的起因 事情的缘由是要比较在不同类型的容器类型(vector、deque、set等等)中,对象是否有moveable的相关构造函数 对容器的各种操作性能的影响。所以要编写一个通用的模版方法,允许接受不同的模版参数,以及不同的数据类型,并能输出调用各种操作(拷贝构造,移动构造,赋值构造)耗费的时间等的时间。 为了方便进行,这里编写了两个简化版本的String类,一个名为MyString的类带有各种移动构造,拷贝,赋值函数(Big Five) #include #include # …

template template parameter alias template iterator traits

C++中的前置后置式自增自减运算符

根据运算符在变量的前面还是后面,可以分为前置(prefix)运算符和后置(postfix)运算符。++i、--i就是前置运算符,i++、i--就是后置运算符。 前置i,++i是先将变量 i 的值加 1,然后再使用这个新值进行计算或赋值操作;后置i,i++则是先使用变量i的原始值进行计算或者赋值,然后再将i的值加1。 在C#中,前置式和后置式++或--,似乎除了语义上的区别之外,效率上没有区别,但在C++中则不一样,《Primer C++》和 《More Effective C++》中都说明,前置式自增自减(++i、--i)的效率比后置式(i++、i--) 通常更快,且至少不慢(prefix will sometimes be faster but never slower then postfix)。 …

prefix postfix overloading

编译器对代码的重排序对程序可能产生的影响

编译器在编译代码或者CPU在执行代码的时候,为了提高编译器和处理器的执行性能,编译器和处理器会对指令重排(reorder)序。 编译器优化的重排序是在编译时期完成的,指令重排序和内存重排序是在CPU运行时进行的。通常情况下,重排序不会产生什么问题,但在涉及到多线程时,可能会遇到问题。本文列举了两个例子来说明编译器重排序,可能对程序造成错误。第一个例子是经典的双检锁,编译器对代码的重排序可能会多线程模式下的代码产生意向不到的错误,这个时候就需要同步锁来强制代码按照我们要求的方式来生成。第二个例子是在使用智能指针管理资源对象时可能产生的资源泄露,编译器的重排序,可能会使得原始对象创建后,和把对象放到智能指针之间会被插入其它的操作,如果这些操作抛出了异常,那么创建出来的原始对象的指针就会丢失,从而产生资源泄露。所以在编写多线程代码以及在对原始资源进行管理的时候,要注意以上问题。 …

Singleton volatile reorder double-check locking lazy initialization

从C++中的迭代器说到左闭合区间

和C#中的IEnumerable接口类似,在C++中,遍历标准容器库比如vector、deque、list等,都需要用到迭代器对象(iterator),根据容器的类型不同以及访问时是否需要读写,迭代器也分为可读写迭代器iterator,只读迭代器const_iterator以及反向迭代器reverse_itertaor。 调用容器类型成员的begin和end方法(或者cbegin,cend,rbegin,rend)方法,这两个方法分别返回指向容器首元素,以及尾元素之后的位置(one past the last element),简称尾后的迭代器。 这里面有一个容易误解的地方在于,end方法返回的迭代器,从来都不会指向容器的最后一个元素,而是指向最后一个元素之后的元素。如果容器v的第一个和最后一个元素分别记为first和last,那么调用v.begin()和v.end()返回的迭代器范围( …

iterator left-inclusive Dijkstra

C++中的声明、定义和初始化

不同于其它语言比如C#,C++中有很多专有的术语,有些术语还十分重要,比如声明、定义、初始化,初始化又有直接初始化和赋值初始化,了解这些术语对于掌握C++非常重要,这里就简单记录一下: 为什么会有声明和定义的区分 C++语言支持分离式编译(seperation compilation)机制,即允许将程序分割为若干文件,每个文件可以被独立编译。如果将程序分为多个文件,则需要有在文件间共享代码的方法,一个文件中的代码可能需要另一个文件中定义的变量。 为了支持分离式编译,C++语言将声明(declaration)和定义(definition)区分开来。声明使得名字为程序所知,一个文件如果想使用别处定义的名字,必须包含对那个名字的声明。而定义则负责创建与名字关联的实体。 变量的声明规定的变量的名字和类型,定义也一样,但除此之外,定义还申请存储空间,也可能会为变量赋予一个初始值。我们通常看到的.h头 …

C++ declaration definition initialization