c++知识点

c++类的默认继承方式是private
类的默认成员类型也是private
函数名字本身就是函数的首地址
explicit关键字的作用就是防止类构造函数的隐式自动转换.,在C++中, 如果构造函数只有一个参数时, 那么在编译的时候就会有一个缺省的转换操作:将该构造函数对应数据类型的数据转换为该类对象. C++中的explicit关键字只能用于修饰只有一个参数的类构造函数, 它的作用是表明该构造函数是显示的, 而非隐式的, 跟它相对应的另一个关键字是implicit, 意思是隐藏的,类构造函数默认情况下即声明为implicit(隐式).

在c中,为了解决一些频繁调用的小函数大量消耗栈空间或是叫栈内存的问题,特别的引入了inline修饰符,表示为内联函数。c/c++中的inline,使用在函数声明处,表示程序员请求编译器在此函数的被调用处将此函数实现插入,而不是像普通函数那样生成调用代码(申请是否有效取决于编译器)。一般地说,这样作的优点是省掉了调用函数的开销;缺点则是可能会增加代所生成目标代码的尺寸(二班的除外,二班情况下,inline函数甚至会返过来降低程序的性能)。

C++中的struct对C中的struct进行了扩充,它已经不再只是一个包含不同数据类型的数据结构了,它已经获取了太多的功能。truct作为数据结构的实现体,它默认的数据访问控制是public的,而class作为对象的实现体,它默认的成员变量访问控制是private的

map的每个成员就是pair

map中插入已有的key,会导致无法插入Because element keys in a map are unique, the insertion operation checks whether each inserted element has a key equivalent to the one of an element already in the container, and if so, the element is not inserted, returning an iterator to this existing element (if the function returns a value

线程是程序最基本的运行单位,而进程不能运行,所以能运行的,是进程中的线程。进程仅仅是一个容器,包含了线程运行中所需要的数据结构等信息。一个进程创建时,操作系统会创建一个线程,这就是主线程,而其他的从线程,却要主线程的代码来创建,也就是由程序员来创建。
一般地,线程的调度(也就是什么时候运行,什么时候暂停运行)由操作系统来负责,但你也可以显式地挂起其他的线程,或继续其他线程的运行。
究竟main函数是进程还是线程呢:
因为它们都是以main()做为入口开始运行的。 是一个线程,同时还是一个进程。在现在的操作系统中,都是多线程的。但是它执行的时候对外来说就是一个独立的进程。这个进程中,可以包含多个线程,也可以只包含一个线程。当用c写一段程序的话,就是在操作系统中起一个进程它包含一个线程。而当用java等开发一个多线程的程序的话,它在操作系统中起了一个进程,但它可以包含多个同时运行的线程。

private在定义类的时候,其类型只能由类的成员函数使用,这样如果类中没有相应的函数,在外面是获取不到该值的,该类的对象也不能直接访问私有变量和私有成员函数,只能通过公有成员函数访问。

访问成员函数**“.”的优先级大于解引用"*"**

所谓的静态链接库,指的是当编译源程序的时候,把相应的库函数也编译进去,生成相应的可执行文件(所谓的可执行文件就是底层硬件可以识别的指令集合),这时的可执行文件是自满足的,不需要借助其他的库便可以独立执行
而动态链接库是指不把库函数编译进去,只有当执行程序的时候,才去调用相应的库函数,这样每个运行的程序(处于运行中的可执行文件)都可以共享一份库函数,大大减少了库函数的内存开销。

构造函数到底是干什么的?”,构造函数是初始化已创建好的对象中成员变量的,而不是创建对象的,不是,绝对不是

当基类的析构函数为虚函数的时候,当我们使用一个基类指针释放派生类对象的时候,虚函数调用机制能够确保我们调用正确的析构函数,然后该析构函数再隐式地调用其基类的析构函数和成员的析构函数。

静态成员函数的优点在于,在不用创建对象(通过类名作用域)的前提下,通过其来访问私有的(保证安全性)静态成员变量。

所谓的静态变量存储在静态存储区域,静态存储区域是指当程序运行时,一旦吧静态变量分配到该存储区域,在整个程序的执行期间,变量的地址值都不会改变。与之对应的动态变量存储在动态存储区域,动态存储区域是指变量地址的有效期只在其作用域内,一旦离开其作用域,相应的空间将会被释放。动态存储区域有栈,堆。

编译时顾名思义就是正在编译的时候.那啥叫编译呢?就是编译器帮你把源代码翻译成机器能识别的代码,如果发现啥错误编译器就告诉你.如果你用微软的VS的话,点下build.那就开始编译,如果下面有errors或者warning信息,那都是编译器检查出来的.所谓这时的错误就叫编译时错误,这个过程中做的啥类型检查也就叫编译时类型检查,或静态类型检查(所谓静态嘛就是没把真把代码放内存中运行起来,而只是把代码当作文本来扫描下).所以有时一些人说编译时还分配内存啥的肯定是错误的说法.
所谓运行时就是代码跑起来了.被装载到内存中去了.(你的代码保存在磁盘上没装入内存之前是个死家伙.只有跑到内存中才变成活的).而运行时类型检查就与前面讲的编译时类型检查(或者静态类型检查)不一样.不是简单的扫描代码.而是在内存中做些操作,做些判断.

c++的这个cast 是借用的冶金中铸造注模的概念,把液态的金属倒入成型的模范这个过程叫cast。编程中把一段数据(数据流)装成某个数据类型,让数据具有某个类型的过程叫做cast。
比如让4个字节变成一个int类型,把int变成4个char这种过程。
基本上和“类型转换”同义,不过cast在c++语言中是从对象封装的视角看这个动作。
所以有动态cast,静态cast等多种cast。

编程语言的类型本质上就是对内存中0-1序列的解释,同一个序列,不同的解释赋予了不同的功能和意义,而所谓的类型转换本质上就是对0-1串的重新解释(reinterpret)

去const属性用const_cast。、
基本类型转换用static_cast。
多态类之间的类型转换用daynamic_cast。
不同类型的指针类型转换用reinterpret_cast。
其中 const_cast 和 reinterpret_cast转换的类型为指针类型。
const_cast一般应用场合是在函数参数为非const指针,而要传入一个const指针,此时就应该使用const_cast去掉传入的const属性

define后面 #与##,#的作用是把接受的输入参数直接作为字符串处理,而不论你输入什么类型的参数,##的作用是把前后两个参数连接起来合并为一个整体作为一个新的变量名

64-bit computing is the use of processors that have datapath widths, integer size, and memory address widths of 64 bits(64-位指的是处理器的数据总线宽度,整型大小,地址总线宽度都为64位)

当类A作为类B的友元时,意味着在B类中声明关键字friend class A,因为友元的单向性,意味着只有类A可以访问类B的成员,而类B不能访问类A的成员

pthread_cond_wait() 用于阻塞当前线程,等待别的线程使用pthread_cond_signal()或pthread_cond_broadcast来唤醒它。

pthread_cond_wait() 必须与pthread_mutex_lock() 配套使用。pthread_cond_wait()函数一进入wait状态就会自动release mutex。当其他线程通过pthread_cond_signal()或pthread_cond_broadcast,把该线程唤醒,使pthread_cond_wait()通过(返回)时,该线程又自动获得该mutex。

在c++里,类和结构体的唯一区别是类里面的变量函数如果没有特殊说明默认是private 而结构体里的默认是public

std::bind中第二个参数应该是对象的指针,且std::bind支持虚函数

类的成员函数不同于普通的函数,因为成员函数指针不能直接调用operator(),它必须被绑定到一个对象或指针,然后才能得到this指针进而调用成员函数。因此bind需要 “牺牲”一个占位符,要求提供一个类的实例、引用或者指针,通过对象作为第一个参数来调用成员函数

初始化列表的成员初始化顺序:
C++初始化类成员时,是按照声明的顺序初始化的,而不是按照出现在初始化列表中的顺序
s t r i n g − > u n s i g n e d   c h a r ∗      s t r c p y ( ( c h a r ∗ ) ( u n s i g n e d   c h a r ∗ ) ,   s t r i n g . c _ s t r ( ) ) string -> unsigned\ char^* \ \ \ \ strcpy((char^*)(unsigned\ char^*), \ string.c\_str()) string>unsigned char    strcpy((char)(unsigned char), string.c_str())
u n s i g n e d   c h a r ∗ − > s t r i n g      s t r i n g = ( c h a r ∗ ) ( u n s i g n e d   c h a r ∗ ) unsigned\ char^* -> string \ \ \ \ string = (char^*)(unsigned\ char^*) unsigned char>string    string=(char)(unsigned char)

所谓的拷贝构造函数:只是在执行构造函数的时候,初始化成员的数据来源于本类的另外一个对象

一个对象可以在同类的另外一个对象的成员函数中直接访问自己的私有成员变量,也就是说public,protected和private的限制是作用在类之间的,而非对象之间

虚函数表存放在全局区(静态区),因为所有的对象共用一份。

在C++下,空类和空结构体的大小是1(编译器相关),这是为什么呢?为什么不是0?

这是因为,C++标准中规定,“no object shall have the same address in memory as any other variable” ,就是任何不同的对象不能拥有相同的内存地址。 如果空类大小为0,若我们声明一个这个类的对象数组,那么数组中的每个对象都拥有了相同的地址,这显然是违背标准的。

由于static修饰的类成员属于类,不属于对象,因此static类成员函数是没有this指针的,this指针是指向本对象的指针。正因为没有this指针,所以static类成员函数不能访问非static的类成员,只能访问 static修饰的类成员。

赋值操作是在两个已经存在的对象间进行的,而初始化是要创建一个新的对象,并且其初值来源于另一个已存在的对象。编译器会区别这两种情况

operator new是用来分配内存的函数,为new操作符调用。能够被重载

placement new是一种特殊的operator new,作用于一块已分配但未处理或未初始化的raw内存

vector的reserve(size_t)函数是程序员用来预估所需的空间,并分配一段size_t大小的连续空间,以此减少由于频繁扩容带来的元素拷贝开销, resize(size_t, value)用来分配空间,并对空间进行初始化

vector中的unique(iterator, iterator)将连续的重复元素移至后面,且返回其中的第一个位置的迭代器,erase(iterator)删除的单个元素和erase(iterator, iterator)删除一段元素。

allocator分配器底层就是利用operator new 函数 , operator new[]函数来分配raw内存,利用placement new来在raw内存上执行某种类型对象的构造函数

构造函数的执行可以分成两个阶段,初始化阶段和计算阶段,初始化阶段先于计算阶段。所有类类型(class type)的成员都会在初始化阶段初始化,即使该成员没有出现在构造函数的初始化列表中。
初始化类的成员有两种方式,一是使用初始化列表,二是在构造函数体内进行赋值操作。使用初始化列表主要是基于性能问题,对于内置类型,如int, float等,使用初始化类表和在构造函数体内初始化差别不是很大,但是对于类类型来说,最好使用初始化列表,为什么呢?由上面的测试可知,使用初始化列表少了一次调用默认构造函数的过程,这对于数据密集型的类来说,是非常高效的。

除了性能问题之外,有些时场合初始化列表是不可或缺的,以下几种情况时必须使用初始化列表

常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面
引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面
没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化。

当虚函数中有默认参数的时候,且子类重写了该虚函数,在利用父类指指针指向子类对象的时候,如果不传入参数,则得到的是父类虚函数的默认参数

c++ STL::Map中如果索引不存在的Key,则会调用对应value的构造函数

const和引用成员变量必须在构造函数中进行初始化

地址的长度和 I n t Int Int 类型的大小不一定相等

拥有纯虚函数的类无法创建对象

:对‘vtable for ’未定义的引用 往往是由于虚函数声明了却未定义导致的

对于基类指针Base* base 和派生类指针 Derive* derive,
D e r i v e ∗   t m p = s t a t i c _ c a s t &lt; D e r i v e ∗ &gt; ( b a s e ) Derive^*\ tmp = static\_cast&lt;Derive*&gt;(base) Derive tmp=static_cast<Derive>(base)
D e r i v e ∗   t m p = d y n a m i c _ c a s t &lt; D e r i v e ∗ &gt; ( b a s e ) Derive^*\ tmp = dynamic\_cast&lt;Derive*&gt;(base) Derive tmp=dynamic_cast<Derive>(base)的异同之处在于当 b a s e base base 实际指向 D e r i v e Derive Derive派生类类型的时候,它们是相同的,但当 b a s e base base 实际指向 B a s e Base Base 基类类型的时候, s t a t i c _ c a s t static\_cast static_cast(静态类型转换)返回转换前的基类指针本身, d y n a m i c _ c a s t dynamic\_cast dynamic_cast(动态类型转换)返回空指针

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值