《深度探索C++对象模型(Inside The C++ Object Model )》学习笔记

来源:http://dsqiu.iteye.com/blog/1669614


之前一直对C++内部的原理的完全空白,然后找到《Inside The C++ Object Model》这本书看了下, 感觉收获很大,因为书写得比较早,有些知识应该要更新,但是还是值得好好研读,由于该书的内容给人比较散的感觉,所以一直想找个时间整理一下,遂成此文,虽然都是抄书上的,但是却让我有了温故而知新的觉悟,附近里有三个好资料,一并共享了!2012年9月2日凌晨 4:31 谢谢 张雨生的歌声的相伴!

 

《深度探索C++对象模型(Inside The C++ Object Model )》学习笔记

 

第一章 关于对象

 

使用class封装之后的布局成本:

class并没有增加成本,data members直接内含在每一个class object之中,就像C struct一样。而member functions虽然被包含在class的声明之内,但是不出现在Object之中。每一个non-inline function 只会产生一个函数实体。至于inline function则会在每一个调用使用的地方产生一个函数实体(在调用点展开函数体)。

class在布局以及存取时间上主要的额外负担是由virtual 引起,包括:

virtual function 机制 用以支持一个有效率的“执行期绑定(runtime binding)”。

virtual base class 用以实现“多次出现在继承体系中的 base class ,有一个单一的被共享的实体”。

当然还有一些剁成继承下的额外负担,发生在“一个 derived class 和其第二或后继之 base class 的转换”之间。

 

C++ 对象模型

在C++对象模型中,nonstatic data members 被放置在每一个class object之内,static data members 则被存放在所以class object 之外。static和nonstaitc function也被放在所有 class object 之外。virtual functions 则以两个步骤支持之:

1.每一个 class 产生出一堆指向 virtual functions 的指针,放在表格之中,这个表格被称为 virtual table(vtbl).

2.每一个 class object被添加一个指针,指向相关的virtual table 。通常这个指针被称为 vptr ,vptr的设定和重置都由每一个 class 的 constructor 、destructor 和 copy assignment 运算符自动完成。

 

C++ 以下列方法支持多态:

1.经由一组隐含的转化操作。例如把一个 derived class 指针转换为一个指向其 public base type的指针。

2.经由 virtual function 机制。

3.经由dynamic_cast和typied运算符。

 

class object 需要多少内存:

1.其 nonstatic data members的总和大小。

2.加上任何犹豫alignment的需求和padding(填补)上去的空间。

3.加上为了支持 virtual 而由内部产生的任何额外负担。

 

指针类型:告诉编译器如何解释某个特定地址中的内存内容及其大小(例如:一个string是传统的8 bytes(包括一个 4byte的字符指针和一个用来表示字符串长度的整数)。转型(cast)其实是一个编译指令,大部分不会改变一个指针所含有的真正地址,它只影响“被指出值内存的大小和其内容”的解释方式。

 

第二章 构造函数语意学

 

2.1 Default Constructor

当编译器需要的时候,default constructor会被合成出来,只执行编译器所需要的任务(将members适当初始化)。

 

带有 Default Constructor 的Member Class Object

编译器的出来是:如果一个class A 内含一个或者一个以上 member class objects ,那么class A 的每一个 constructor 必须调用每一个member classes 的default constructor 。编译器会扩张已存在的constructors,在其中安插一些代码,使得 user code在被执行之前,先调用(调用顺序一member objects在class 的声明次序一致)必要的 default constructors。

 

带有 Default Constructor 的 Base class

编译器会在 Member Class Object 的default constructor 被插入调用之前,调用(调用次序根据他们的声明次序)所有 base class constructor 的default constructor 。

 

带有一个 Virtual Function 的Class

下面两种情况同样需要合成default constructor:

1.class 声明(或继承)一个 virtual function。

2.class派生自一个继承串链,其中一个或者更多的 virtual base class。

扩展(constructor)操作会在编译期间发生:

1.一个virtual function table 会被编译器产生出来,内放class 的virtual functions 的地址。

2.在每一个 class object 中,一个额外的pointer member(vptr)会被编译器合成出来,内含相关的class vtbl的地址。

 

带有一个 Virtual Base Class 的class

Virtual base class的实现法在不同编译器之间有很大差异,然而,每一个实现的共同点在于必须使 virtual base class 在其每一个 derived class object中的位置,能够在执行期准备妥当。对于class所定义的每一个constructor 编译器都会安插那些“允许每一个virtual base class 的执行期存取操作”的码。

 

总结

以上四种情况,会导致“编译器必须为未声明constructor 的class 合成一个default constructor ”,这只是编译器(而非程序)的需要。它之所以能够完成任务,是借着“调用member object 或base class的default constructor ”或是“为每一个object初始化其 virtual function 机制或virtual base class 机制”完成。至于没有存在这四种情况而又没有生命constructor的class 实际上是不会被合成出来的。

在合成的default constructor 中,只有base class subobjects(子对象)和member class objects会被初始化。所有其他的nonstatic data member ,如整数,整数指针,整数数组等是不会被初始化的,这些初始化操作对程序是必须的,但对编译器则并非需要的。

C++新手一般有两个误解:

1.任何class 如果没有定义default constructor ,就会被合成出来一个。

2.编译器合成出来的default constructor 会明确设定 class 内每一个data member的默认值。

 

2.2 Copy Constructor 

有三种情况,会以一个object的内容作为另一class object的初值。

1.最明显的当然是对一个object做明确的初始化操作。

2.当object被当做参数交给某个函数

3.当函数返回一个class object。

这三种情况需要有 copy constructor。

 

Default Memberwise Initialization

如果class 没有提供一个 explicit copy constructor时,当class object以“相同的另一个object作为初值是,其内部是以所谓的default memberwise initialization方式完成的。也就是把每一个内建的或派生的 data member(例如一个数组或指针)的值,从某个object拷贝一份到另一个object上,但不拷贝其具体内容。例如只拷贝指针地址,不拷贝一份新的指针指向的对象,这也就是浅拷贝,不过它并不会拷贝其中member class object,而是以递归的方式实行memberwise initialization。

这种递归的memberwise initialization是如何实现的呢?

答案就是Bitwise Copy Semantics和default copy constructor。如果class展现了Bitwise Copy Semantics,则使用bitwise copy(bitwise copy semantics编译器生成的伪代码是memcpy函数),否则编译器会生成default copy constructor。

那什么情况下class不展现Bitwise Copy Semantics呢?有四种情况:

1.当class内含有一个member class object,而这个member class 内有一个默认的copy 构造函数[不论是class设计者明确声明,或者被编译器合成]

2.当class 继承自 一个base class,而base class 有copy构造函数[不论是class设计者明确声明,或者被编译器合成]

3.当一个类声明了一个或多个virtual 函数

4.当class派生自一个继承串链,其中一个或者多个virtual base class

下面我们来理解这四种情况为什么不能使用bitwise copy,以及编译器生成的copy constructor都干了些什么。

在前2种情况下,编译器必须将member或者base class的“ copy constructor的调用操作”安插到被合成的copy constructor中。

 

重新设定Virtual Table 的指针

第3种情况下,因为class 包含virtual function, 编译时需要做扩张操作:

1.增加virtual function table,内含有一个有作用的virtual function的地址;

2.创建一个指向virtual function table的指针,安插在class object内。

所以,编译器对于每一个新产生的class object的vptr都必须被正确地赋值,否则将跑去执行其他对象的function了,其后果是很严重的。因此,编译器导入一个vptr到class之中时,该class 就不在展现bitwise semantics,必须合成copy Constructor并将vptr适当地初始化。

 

处理Virtual Base Class Subobject

virtual base class的存在需要特别处理。一个class object 如果以另一个 virtual base class subobject那么也会使“bitwise copy semantics”失效。

每一个编译器对于虚拟继承的支持承诺,都是表示必须让“derived class object 中的virtual base class subobject 位置”在执行期就准备妥当,维护“位置的完整性”是编译器的责任。Bitwise copy semantics 可能会破坏这个位置,所以编译器必须自己合成出copy constructor。

 

这也就是说,拷贝构造函数和默认构造器一样,需要的时候会进行构建,而并非程序员不写编译器就帮着构建。

 

2.4 初始化列表

下面四种情况必须使用初始化列表来初始化class 的成员:

1.当初始化一个reference member时;

2.当初始化一个const member时;

3.当调用一个base class 的 constructor ,而它拥有一组参数(其实就是自定义的构造函数)时;

      4.当调用一个 member class 的 constructor,而它拥有一组参数时。

不过,初始化的顺序是class members声明次序决定的,不是由初始化列表决定的。

 

第三章 Data 语意学

 

 

3.2 Data Member 的布局

nonstatic data members 在class object中的排列顺序将和其声明的顺序一样的。但C++ standard 允许编译器将多个access sections之中的data members自由排列,不必在乎他们的出现在class中的声明顺序。

 

3.3 Data Member 的存取

每一个member 的存取许可(private public protected),以及与class的关联,并不会导致任何空间上或执行时间上的额外负担——不论是在个别的class objects 或是在static data member 本身。

static data members 被视为global变量,只有一个实体,存放在程序的data segment之中,每次取static member 就会被内部转化为对该唯一的extern 实体的直接参考操作。若取一个static data member的地址,会得到一个数据类型的指针,而不是只想起class member的指针。

nonstatic data members 欲对一个nonstatic data member 进行存取操作,编译器需要吧class object的起始地址加上data member的偏移量(在编译事情就可以获知)。

 

3.4 继承与Data Member

 

只要继承不要多态

这种情况并不会增加空间或存储时间上的额外负担。这种情况base class和derived class的objects都是从相同的地址开始,其差异只在于derived object 比较大,用以容纳自建的nonstatic data members,把一个derived class object指定给base class 的指针或引用,并不需要编译器去调停或修改地址,它很滋润的可以发生,而且提供了最佳执行效率。

 

加上多态

这种情况会带来空间和存取时间的额外负担:

1.导入一个和virtual table ,用来存储它所声明的每一个virtual functions的地址。

2.在每一个class object中导入一个vptr,提供执行期的链接,使每一个object能够找到相应的virtual table。

3.加强constructor,使它能够为vptr设定初始值,让它指向class 所对应的virtual table 。

4.加强destructor,使它能够消抹“指向class 相关virtual table”的vptr。

 

多重继承

对于一个多重派生对象,将其地址指定给“最左端(第一个)base class的指针”,情况和单一继承时相同,因为二者都指向了相同的起始地址,至于第二个或后面的base class 的地址指定操作,则需要将地址修改过:加上(或减去,如果是downcast)介于中间的base class subobject(s)的大小。

如果要存取第二个(或后面)的base class 中的一个data member ,不需要付出额外的成本,因为members的位置在编译时就固定了,因此存取member只是一个简单的offset的运算。

 

虚拟继承

class如果含有一个或多个virtual base class subobjects将被分割为两部分:一个不变局部和一个共享局部。不变局部中的数据,总是能有固定的offset,这部分可以被直接存取,至于共享部分,所表现的就是virtual base class subobject ,这个部分数据,其位置因为每次派生操作而有变化,所以只能间接存取。

 

如果没有virtual functions的情况下,它们和C struct完全一样。

 

第四章 Function 语意学

 

4.1 Member的各种调用方式

 

Nonstatic Member Functions

实际上编译器是将member function被内化为nonmember的形式,经过下面转化步骤:

1.给函数添加额外参数——this。

2.将对每一个nonstaitc data member的存取操作改为this指针来存取。

3.将member function 重写成一个外部函数。对函数名精选mangling 处理,使之成为独一无二的语汇。

 

Virtual Member Functions

ptr->f();   //f()为virtual member function

内部转化为

(*ptr->vptr[1](ptr);

其中:

vptr表示编译器产生的指针,指向virtual table。它被安插在每一个声明有(或继承自)一个或多个virtual functions 的class object 中。

1 是virtual table slot的索引值,关联到normalize()函数。

第二个ptr表示this指针。

 

Static Member Functions

不能被声明为 const volatile 或virtual。

一个static member function 会提出于class声明之外,并给予一个经过mangling的适当名称。如果取一个static member function 的地址,获得的是其在内存的位置也就是地址,而不是一个指向“class member function”的指针,如下:

&Point::count(); 

会得到一个数值,类型是:

unsigned int(*)();

而不是:

unsigned int(Point::*)();

 

4.2 Virtual Member Funcitons

C++中,多态表示以“一个public base class 的指针(或reference),寻址出一个derived class object”。

每一个class 只会有一个virtual table,每一个table 含有对应的class object中所有active virtual functions 函数实体地址。这些active virtual function 包括:

1.这个class 所定义的函数实体(改写(overriding)一个可能存在的base class virtual function函数实体。

2.继承自base class 的函数实体(不被derived class改写)

3.一个pure_virtual_called()。

一个类继承函数virtual table的三种可能性:

1.继承base class 所声明的virtual functions的函数实体。正确地说,是该函数实体的地址会被拷贝到derived class的virtual table相对应的slot之中。

2.使用自己的函数实体。这表示它自己的函数实体地址必须放在对应的slot之中。

3.可以加入一个新的virtual function。这时候virtual table 的尺寸会增大一个slot放进这个函数实体地址。

 

编译时期设定virtual function的调用:

一般而言,我并不知道ptr 所指对象的真正类型。然而可以经由ptr 可以存取到该对象的virtual table。

虽然我不知道哪个Z()函数实体被调用,但知道每一个Z()函数地址都被放置slot 4的索引。

这样我们就可以将

ptr->z();

转化为:(*ptr->vptr[4])(ptr);

唯一一个在执行期才能知道的东西是:slot4所指的到底是哪一个z()函数实体。

 

多重继承下的 Virtual Functions

在多重继承中支持virtual functions,其复杂度围绕在第二个及其后面的base class 上,以及“必须在执行期调整this 指针”这一点。一般规则是,经由指向“第二或后继base class 的指针”来调用derived class virtual function。调用操作连带的“必要的this指针调整”操作,必须在执行期完成。

 

虚拟继承下的 Virtual Functions

 

4.3 函数的效能

nonmemeber、static member或nonstatic member函数都被转换为完全相同形式,所以三者效率完全相同。

 

4.4 指向Member Function的指针

取一个nonstatic data member的地址,得到的结果是该member在class 布局中的bytes位置,所以它需要绑定于某个class object的地址上,才能够被存取。

取一个nonstatic member function的地址,如果该函数是nonvirtual,则得到的是内存的真正地址,然后这个值也是不完全的,也需要绑定于某个class object的地址上,才能够调用函数。

 

支持“指向Virtual Member Function”之指针

对于一个virtual function,其地址在编译时期是未知的,所能知道的仅是virtual function在其相关之virtual table的索引值,也就是说,对于一个virtual member function 取其地址,所能获得的只是一个索引值。

 

4.5 Inline Funcitons

形参   传入参数,直接替换 传入常量,连替换都省了,直接变成常量 传入函数运行结果,则需要导入临时变量

局部变量  局部变量会被mangling,以便inline函数被替换后名字唯一 也就是说一次性调用N次,就会出现N个临时变量……程序的体积会暴增

 

第五章 构造、解构、拷贝 语意学

 

继承体系下的对象构造

constructor的调用伴随了哪些步骤:

1.初始化列表(member initialization list)的data members初始化操作会被放进constructor的函数本身,并以membs的声明顺序为顺序。

2.如果有一个member并没有在初始化列表中,但它在一个default constructor,那么该default constructor 必须被调用(手动)。

3.在那之前,如果class object有virtual table pointer(s),它(们)必须被设定初始值,指定适当的virtual table(s)。

4.在那之前,所有上一层的base class constructors 必须被调用,以base class 的声明顺序为顺序(与初始化列表的顺序没有关联)。

如果base class 被列于初始化列表中,那么任何明确指定参数都应该传递过去。

如果base class 没有列于初始化列表,那么调用default constructor。

如果base class 是多重继承下的第二或后面的base class ,那么this指针必须有所调整。

5.在那之前,所有 virtual base class constructors 必须被调用,从左到右,从最深到最浅。

如果class 被列于初始化列表中,那么如果有任何明确指定的参数,都应该传递过去,若没有列于初始化列表中,则调用default constructor。

此外,class中的每一个virtual base class subobject的偏移量必须在执行期可存取。

如果class object 是最底层的class,某constructors可能被调用;某些用以支持这个行为的机制必须被放进来。

 

对象复制语意学

当设计一个class,并以一个class object 指定另一个class object时,有三种选择:

1.什么都不做,实施默认行为。

2.提供一个explicit copy assignment operator。

3.明确拒绝一个class object指定给另一个class object。

一个class对于默认的copy assignment operator,在以下情况下不会表现出 bitwise copy语意:

1.当一个class的base class 有一个copy assignment operator时,

2.当一个class 的member object,而其class 有一个copy assignment operator时,

3.当一个class 声明了任何virtual functions时,

4.当class继承一个virtual base class 时。

 

vptr语意学

vptr在constructor何时被初始化?在base class constructors调用操作之后,但是在程序员供应的码或是初始化列表中所列的members初始化操作之前。

 

解构语意学

destructor被扩展的方式:

1.destructor的函数本身首先被执行。

2.如果class拥有member class objects,而后拥有destructor,那么它们会以声明顺序的相反顺序被调用。

3.如果object内带一个vptr,则现在被重新设定,指向适当的base class virtual table。

4.如果有任何直接的(上一层)nonvirtual base classes 拥有destructor ,它们会以声明顺序相反顺序调用。

5.如果有任何virtual base classes 拥有destructor,而当前讨论的这个class 是最尾端的class,那么它们会以其原来顺序相反顺序被调用。

 

第六章 执行期语意学

 

第七章 ……

 

补充:类型向上转型和多态的混淆

 

 

构造这样的一个继承体系:

class Base {

public: virtual ~Base() {}

virtual void show() { cout << "Base" << endl; }

};

class Derived : public Base {

public: void show() { cout << "Derived" << endl; }

};

子类Derived类重写了基类Base中的show方法。 编写下面的测试代码:

Base b; Derived d;

b.show(); d.show();

结果是:

Base

Derived

Base的对象调用了Base的方法,而Derived的对象调用了Derived的方法。因为直接用对象来调用成员函数时不会开启多态机制,故编译器直接根据b和d各自的类型就可以确定调用哪个show函数了,也就是在这两句调用中,编译器为它们每一个都确定了一个唯一的入口地址。这实际上类似于一个重载多态,虽然这两个show函数拥有不同的作用域。 那这样呢: Base b; Derived d; b.show(); b = d; b.show(); 现在,一个Base的对象被赋值为子类Derived的对象。

 

 

那这样呢:

Base b; Derived d;

b.show(); b = d; b.show();

现在,一个Base的对象被赋值为子类Derived的对象。

结果是:

Base

Base

对于熟悉Java的人而言,这不可理解。但实际上,C++不是Java,它更像C。“b = d”的意思,并不是Java中的“让一个指向Base类的引用指向它的子类对象”,而是“把Base类的子类对象中的Base子对象分割出来,赋值给b”。所以,只要b的类型始终是Base,那么b.show()调用的永远都是Base类中的show函数;换句话说,编译器总是把Base中的那个show函数的入口地址作为b.show()的入口地址。这根本就没用上多态。

单继承下的重写多态

那我们再这样:

Base b; Derived d;

Base *p = &b;

p->show();

p = &d;

p->show();

这时,结果就对了:

Base

Derived

p是一个指向基类对象的指针,第一次它指向一个Base对象,p->show()调用了Base类的show函数;而第二次它指向了一个Derived对象,p->show()调用了Derived类的show函数。

总结:也就是说,只有是指针或者引用才是真正的多态,将子对象赋给父类对象其实类型向上转型……

 

个人觉得C++容易弄混淆的地方(持续更新):

1.const和指针的修饰问题

const char * a;   //一个指针a指向const char

char const *a;      //这两个是a指向的内容是常量,不能改变

char * const a;     //首先a 是指针然后还是const

const (char*) a;     //这两个是a指针本身是常量,指针本身不能改变

其实,可以看出如果const修饰的char(也就是类型本身或者是 *variable对指针的解引用)就是指针指向的内容是常量,反之就是修饰指针本身的。那我们可以总结一个识别方法就是:看const 两边(当然有的只有一边)的类型是类型(指针指向的内容)就是类型变量本身是常量(如const char * a和char const *a 的const两边是char,*a)。

当然两者都是常量就是:const char * const a;第一个const是类型常量,第二个才是指针常量。同样给出 const char &a ;const char *a;在传递参数时使用。

2.数组和指针的组合问题

char * a[M];  这是指针数组,就是每一个元素是指针的数组,每个元素都要初始化。a[M]一看就是数组,这个数组每一个元素是char *,所以可以将char *扩展为一维数组然后a[M]就是二维数组了。其实就是M个指针。

char (*a)[N];  这是一个指针,这个指针指向N个char元素,即指向数组的指针,其实就是一个指针。把(*a)看着一个变量,这个变量是指向N个元素的指针,所以只是一个一维数组。把char (*a)[N]看成是char b[N]就可以了。

同理,也可以用修饰的道理来区分,可以自行体会。具体二维数组的动态分配的更多精彩可以查阅我的另一个博客http://dsqiu.iteye.com/blog/1683142

3.C++变量的初始化

对于内置类型局部变量不进行初始化,但是分配地址,全局变量会进行默认初始化。对于类类型局部变量(没有显式初始化)会进行默认初始化(有默认构造函数,否则报错),但其内部的内置数据成员不会进行初始化(如果在默认构造函数没有进行初始化)。数组也是同样。



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/496757.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

arp病毒利用的Javascript技术

本文的目的是探讨JS相关技术&#xff0c;并不是以杀毒为主要目的&#xff0c;杀毒只是为讲解一些JS做铺垫的&#xff0c;呵呵&#xff0c;文章有点长&#xff0c;倒杯咖啡或者清茶慢慢看&#xff0c;学习切勿急躁&#xff01; 最近公司的网络中了这两天闹的很欢的ARP病毒&#…

C++学习之路 | PTA乙级—— 1083 是否存在相等的差 (20 分)(精简)

1083 是否存在相等的差 (20 分) 给定 N 张卡片&#xff0c;正面分别写上 1、2、……、N&#xff0c;然后全部翻面&#xff0c;洗牌&#xff0c;在背面分别写上 1、2、……、N。将每张牌的正反两面数字相减&#xff08;大减小&#xff09;&#xff0c;得到 N 个非负差值&#xf…

C++的黑科技(深入探索C++对象模型)

来源&#xff1a;http://www.cnblogs.com/qiaozhoulin/p/5227673.html “如何产生一个不能被继承的类”&#xff0c;这道题我反反复复只想到&#xff0c;将父类的构造函数私有&#xff0c;让子类不能调用&#xff0c;最后归结出一个单例模式&#xff0c;但面试官说&#xff0c;…

物联网测试完整解决方案 | 为你的芯片、模块、终端保驾护航

来源&#xff1a;物联网智库2017年被视为物联网商用元年。窄带物联网标准正式冻结&#xff0c;国内运营商重点布局以及设备制造商的强力推动……自此&#xff0c;物联网规模化商用迈入了快车道。然而&#xff0c;与传统的智能手机类似&#xff0c;基于NB-IoT技术的物联网设备在…

mbp网速很慢_苹果笔记本上网很慢怎么回事?macbook无线上网慢的解决方法

Mac笔记本连接到WiFi之后,发现网速很慢&#xff0c;简直让人无法接受&#xff0c;打开一个页面都要等上好几分钟。苹果笔记本上网很慢的原因有很多&#xff0c;检查一下WiFi信号是满格&#xff0c;那么排除所在网络带宽限制的原因。想要解决此问题也简单&#xff0c;只要修改手…

Java并发编程实战~CyclicBarrier

用 CyclicBarrier 实现线程同步 线程 T1 和 T2 要做到步调一致&#xff0c;都完成后通知到线程 T3 创建了一个计数器初始值为 2 的 CyclicBarrier&#xff0c;你需要注意的是创建 CyclicBarrier 的时候&#xff0c;我们还传入了一个回调函数&#xff0c;当计数器减到 0 的时候…

梦中的婚礼

让女孩听了陶醉的曲子梦中的婚礼 :http://player.youku.com/player.php/sid/XMjA1ODc3MTI/v.swf 电脑键盘弹奏 梦中的婚礼 :http://player.youku.com/player.php/sid/XMzMzNzQyMDA/v.swf 转载于:https://www.cnblogs.com/cube/archive/2008/07/19/1246736.html

C++学习之路 | PTA乙级—— 1084 外观数列 (20 分)(精简)

1084 外观数列 (20 分) 外观数列是指具有以下特点的整数序列&#xff1a; d, d1, d111, d113, d11231, d112213111, … 它从不等于 1 的数字 d 开始&#xff0c;序列的第 n1 项是对第 n 项的描述。比如第 2 项表示第 1 项有 1 个 d&#xff0c;所以就是 d1&#xff1b;第 2 项是…

DeepMind提出图形的「深度生成式模型」,可实现「任意」图形的生成

原文来源&#xff1a;arXiv作者&#xff1a;Yujia Li、Oriol Vinyals、Chris Dyer、Razvan Pascanu、Peter Battaglia 「雷克世界」编译&#xff1a;嗯~阿童木呀、KABUDA一般来说&#xff0c;图形是基本的数据结构&#xff0c;它在诸如知识图、物理和社会交互、语言和化学等许多…

eviews曲线图怎么做_【干货速递】Eviews:你不可不知的经典问答!

更多精彩内容请关注211统计课堂计量经济学是分析啥的&#xff1f;01计量经济学的主要用途或目的主要有两个方面&#xff1a;1.理论检验。这是计量经济学用途最为主要的和可靠的方面。这也是计量经济学本身的一个主要内容。2.预测应用。从理论研究和方法的最终目的看&#xff0c…

感恩的心

不气歌&#xff08;一&#xff09; 他人气我我不气&#xff0c;我本无心他来气。 倘若生气中他计&#xff0c;气出病来无人替。 请来医生把病治&#xff0c;反说气病治非易。 茶不思来饭无味&#xff0c;通宵达旦不入睡。 倘你伸腿离我去&#xff0c;撇我一人活受罪。 奉…

Python-OpenCV 图像与视频处理

来源&#xff1a;https://segmentfault.com/a/1190000003742481 一直断断续续的用过几次 OpenCV&#xff0c;感觉熟练掌握它的使用方法已经变的非常必要了&#xff0c;正好找到一个很不错的英文教程&#xff0c;就以此为起点&#xff0c;详细记录一下对 OpenCV 的学习过程吧。 …

C++学习之路 | PTA乙级—— 1086 就不告诉你 (15 分)(精简)

1086 就不告诉你 (15 分) 做作业的时候&#xff0c;邻座的小盆友问你&#xff1a;“五乘以七等于多少&#xff1f;”你应该不失礼貌地围笑着告诉他&#xff1a;“五十三。”本题就要求你&#xff0c;对任何一对给定的正整数&#xff0c;倒着输出它们的乘积。 输入格式&#xf…

Java并发编程实战~并发容器

在容器领域一个容易被忽视的“坑”是用迭代器遍历容器&#xff0c;例如在下面的代码中&#xff0c;通过迭代器遍历容器 list&#xff0c;对每个元素调用 foo() 方法&#xff0c;这就存在并发问题&#xff0c;这些组合的操作不具备原子性。 List list Collections.synchronize…

12c oracle 激活_Windows运维之Windows server 2016 安装及ORACLE 12C 安装

本文主要向大家介绍了Windows运维之Windows server 2016 安装及ORACLE 12C 安装&#xff0c;通过具体的内容向大家展现&#xff0c;希望对大家学习Windows运维有所帮助。首先创建虚拟机&#xff0c;选择windows server 2016启动虚拟机&#xff0c;进入安装界面&#xff0c;语言…

工信部:筹建全国首个区块链和分布式记账标准化技术委员会

作者&#xff1a;李秀琴在3.15即将来临之时&#xff0c;我国工信部给区块链行业又带来了一大利好消息。3月12日&#xff0c;工业和信息化部&#xff08;以下简称工信部&#xff09;在官网发布公告称&#xff0c;其正在就筹建全国区块链和分布式记账技术标准化技术委员会事宜开展…

PowerDesigner11技巧

文章来源&#xff1a;http://blog.csdn.net/edeed/archive/2006/02/10/596271.aspx 1、安装PD v11.0版 2、由pdm生成建表脚本时&#xff0c;字段超过15字符就发生错误&#xff08;oracle&#xff09; 原因未知&#xff0c;解决办法是打开PDM后&#xff0c;会出现Database的菜单…

C++学习之路 | PTA乙级—— 1087 有多少不同的值 (20 分)(精简)

1087 有多少不同的值 (20 分) 当自然数 n 依次取 1、2、3、……、N 时&#xff0c;算式 ⌊n/2⌋⌊n/3⌋⌊n/5⌋ 有多少个不同的值&#xff1f;&#xff08;注&#xff1a;⌊x⌋ 为取整函数&#xff0c;表示不超过 x 的最大自然数&#xff0c;即 x 的整数部分。&#xff09; 输入…

Python 的 requests 库的用法

Python爬虫利器一之Requests库的用法&#xff1a;http://cuiqingcai.com/2556.html Python利用Requests库写爬虫&#xff08;一&#xff09;&#xff1a;http://www.jianshu.com/p/e1f8b690b951 Python-第三方库requests详解&#xff1a;http://blog.csdn.net/shanzhizi/articl…

Java并发编程实战~Lock

再造管程的理由 synchronized导致死锁问题&#xff0c;提出了一个破坏不可抢占条件方案&#xff0c;但是这个方案 synchronized 没有办法解决。原因是 synchronized 申请资源的时候&#xff0c;如果申请不到&#xff0c;线程直接进入阻塞状态了&#xff0c;而线程进入阻塞状态…