1. C++对象模型

所有的非静态数据成员存储在对象本身中。所有的静态数据成员、成员函数(包括静态与非静态)都置于对象之外。另外,用一张虚函数表(virtual table)存储所有指向虚函数的指针,并在表头附加上一个该类的type_info对象,在对象中则保存一个指向虚函数表的指针。如下图:

image17_thumb1.png

一个类的对象的内存大小包括:

  1. 所有非静态数据成员的大小。
  2. 由内存对齐而填补的内存大小。
  3. 为了支持virtual有内部产生的额外负担。
    如下类:

 

class ZooAnimal {  
public:  ZooAnimal();  virtual ~ZooAnimal();  virtual void rotate();  
protected:  int loc;  String name;  
};

在32位计算机上所占内存为16字节:int四字节,String8字节(一个表示长度的整形,一个指向字符串的指针),以及一个指向虚函数表的指针vptr。对于继承类则为基类的内存大小加上本身数据成员的大小。在cfront中其内存布局如下图:

image21_thumb.png

2. C++构造函数

通常很多C++程序员存在两种误解:

  • 没有定义默认构造函数的类都会被编译器生成一个默认构造函数。
  • 编译器生成的默认构造函数会明确初始化类中每一个数据成员。

C++标准规定:如果类的设计者并未为类定义任何构造函数,那么会有一个默认构造函数被暗中生成,而这个暗中生成的默认构造函数通常是不做什么事的(无用的),下面四种情况除外。

1).包含有带默认构造函数的对象成员的类
若一个类X没有定义任何构造函数,但却包含一个或以上定义有默认构造函数的对象成员,此时编译器会为X合成默认构造函数,该默认函数会调用对象成员的默认构造函数为之初始化。如果对象的成员没有定义默认构造函数,那么编译器合成的默认构造函数将不会为之提供初始化。例如类A包含两个数据成员对象,分别为:string strchar *Cstr,那么编译器生成的默认构造函数将只提供对string类型成员的初始化,而不会提供对char*类型的初始化。

假如类X的设计者为X定义了默认的构造函数来完成对str的初始化,形如:A::A(){Cstr=”hello”};因为默认构造函数已经定义,编译器将不能再生成一个默认构造函数。但是编译器将会扩充程序员定义的默认构造函数——在最前面插入对初始化str的代码。若有多个定义有默认构造函数的成员对象,那么这些成员对象的默认构造函数的调用将依据声明顺序排列。

2).继承自带有默认构造函数的基类的类
如果一个没有定义任何构造函数的类派生自带有默认构造函数的基类,那么编译器为它定义的默认构造函数,将按照声明顺序为之依次调用其基类的默认构造函数。若该类没有定义默认构造函数而定义了多个其他构造函数,那么编译器扩充它的所有构造函数——加入必要的基类默认构造函数。另外,如果该类满足一的条件,那么编译器会将基类的默认构造函数代码加在对象成员的默认构造函数代码之前。

3).带有虚函数的类")
带有虚函数的类,与其它类不太一样,因为它多了一个vptr,而vptr的设置是由编译器完成的,因此编译器会为类的每个构造函数添加代码来完成对vptr的初始化。

4).带有一个虚基类的类
在这种情况下,编译器要将虚基类在类中的位置准备妥当,提供支持虚基类的机制。也就是说要在所有构造函数中加入实现前述功能的的代码。没有构造函数将合成以完成上述工作。

总结:简单来讲编译器会为构造函数做的一点事就是调用其基类或成员对象的默认构造函数,以及初始化vprt以及准备虚基类的位置。

总的来说,编译器将对构造函数动这些手脚:

  • 如果类虚继承自基类,编译器将在所有构造函数中插入准备虚基类位置的代 码和提供支持虚基类机制的代码。
  • 如果类声明有虚函数,那么编译器将为之生成虚函数表以存储虚函数地址,并将虚函数指(vptr)的初始化代码插入到类的所有构造函数中。
  • 如果类的父类有默认构造函数,编译将会对所有的默认构造函数插入调用其父类必要的默认构造函数。必要是指设计者没有显示初始化其父类,调用顺序,依照其继承时声明顺序。
  • 如果类包含带有默认构造函数的对象成员,那么编译器将会为所有的构造函数插入对这些对象成员的默认构造函数进行必要的调用代码,所谓必要是指类设计者设计的构造函数没有对对象成员进行显式初始化。成员对象默认构造函数的调用顺序,依照其声明顺序。
  • 若类没有定义任何构造函数,编译器会为其合成默认构造函数,再执行上述四点。

需要说明的是,从概念来上来讲,每一个没有定义构造函数的类都会由编译器来合成一个默认构造函数,以使得可以定义一个该类的对象,但是默认构造函数是否真的会被合成,将视是否有需要而定。C++ standard 将合成的默认构造函数分为 trivial 和 notrivial 两种,前文所述的四种情况对应于notrivial默认构造函数,其它情况都属于trivial。对于一个trivial默认构造函数,编译器的态度是,既然它全无用处,干脆就不合成它。在这儿要厘清的是概念与实现的差别,概念上追求缜密完善,在实现上则追求效率,可以不要的东西就不要。

3. 拷贝构造函数(copy constuctor)

当一个类对象以另一个同类实体作为初值时,大部分情况下会调用拷贝构造函数。一般是这三种具体情况:

  • 显式地以一个类对象作为另一个类对象的初值,形如X xx=x;
  • 当类对象被作为参数交给函数时。
  • 当函数返回一个类对象时。
    后两种情形会产生一个临时对象。

编译器何时合成拷贝构造函数
并不是所有未定义有拷贝构造函数的类编译器都会为其合成拷贝构造函数,编译器只有在必要的时候才会为其合成拷贝构造函数。
如果一个类没有定义拷贝构造函数,通常按照“成员逐一初始化(DefaultMemberwise Initialization)”(成员逐一初始化(Default Memberwise Initialization)具体的实现方式则是位逐次拷贝(Bitwise copy semantics))的手法来解决“一个类对象以另一个同类实体作为初值”——也就是说把内建或派生的数据成员从某一个对象拷贝到另一个对象身上,如果数据成员是一个对象,则递归使用“成员逐一初始化(Default Memberwise Initialization)”的手法。
有以下几种情况之一,位逐次拷贝将不能胜任或者不适合来完成“一个类对象以另一个同类实体作为初值”的工作。此时,如果类没有定义拷贝构造函数,那么编译器将必须为类合成一个拷贝构造函数:

  • 当类内含一个成员对象,而后者的类声明有一个拷贝构造函数时(不论是设计者定义的还是编译器合成的)。
  • 当类继承自一个声明有拷贝构造函数的类时(同样,不论这个拷贝构造函数是被显示声明还是由编译器合成的)。
  • 类中声明有虚函数。
  • 当类的派生串链中包含有一个或多个虚基类。

对于前两种情况,不论是基类还是对象成员,既然后者声明有拷贝构造函数时,就表明其类的设计者或者编译器希望以其声明的拷贝构造函数来完成“一个类对象以另一个同类实体作为初值”的工作,而设计者或编译器这样做——声明拷贝构造函数,总有它们的理由,而通常最直接的原因莫过于因为他们想要做一些额外的工作或“位逐次拷贝”无法胜任。
对于有虚函数的类,如果两个对象的类型相同那么位逐次拷贝其实是可以胜任的。但问题将出现在,如果基类由其继承类进行初始化时,此时若按照位逐次拷贝来完成这个工作,那么基类的vptr将指向其继承类的虚函数表,这将导致无法预料的后果——调用一个错误的虚函数实体是无法避免的,轻则带来程序崩溃,更糟糕的问题可能是这个错误被隐藏了。所以对于有虚函数的类编译器将会明确的使被初始化的对象的vptr指向正确的虚函数表。因此有虚函数的类没有声明拷贝构造函数,编译将为之合成一个,来完成上述工作,以及初始化各数据成员,声明有拷贝构造函数的话也会被插入完成上述工作的代码。
对于继承串链中有虚基类的情况,问题同样出现在继承类向基类提供初值的情况,此时位逐次拷贝有可能破坏对象中虚基类子对象的位置。

4. 命名返回值优化和成员初始化列表

命名返回值优化
对于一个如foo()这样的函数,它的每一个返回分支都返回相同的对象,编译器有可能对其做Named return Value优化(下文都简称NRV优化),方法是以一个引用参数result取代返回对象。

foo()的原型:
X foo()
{
X xx;
if(...)
returnxx;
else
returnxx;
}

优化后的foo()以result取代xx:
void foo(X &result)
{
result.X::X();
if(...)
{
//直接处理result
return;
}
else
{
//直接处理result
return;
}
}

对比优化前与优化后的代码可以看出,对于一句类似于X a = foo()这样的代码,NRV优化后的代码相较于原代码节省了一个临时对象的空间(省略了xx),同时减少了两次函数调用(减少xx对象的默认构造函数和析构函数,以及一次拷贝构造函数的调用,增加了一次对a的默认构造函数的调用)。

成员初始化列表
对于初始化队列,我相信厘清一个概念是非常重要的:在构造函数中对于对象成员的初始化发生在初始化队列中——或者我们可以把初始化队列直接看做是对成员的定义,而构造函数体中进行的则是赋值操作。所以不难理解有四种情况必须用到初始化列表:

  • 有const成员
  • 有引用类型成员
  • 成员对象没有默认构造函数
  • 基类对象没有默认构造函数

前两者因为要求定义时初始化,所以必须明确的在初始化队列中给它们提供初值。后两者因为不提供默认构造函数,所有必须显示的调用它们的带参构造函数来定义即初始化它们。显而易见的是当类中含有对象成员或者继承自基类的时候,在初始化队列中初始化成员对象和基类子对象会在效率上得到提升——省去了一些赋值操作嘛。

最后,一个关于初始化队列众所周知的陷阱,初始化队列的顺序,初始化列表中成员初始化的顺序和列表中的顺序无关,只与成员在对象中声明的顺序有关。

 

class X{int  i;int  j;
public:X(int  val): j(val), i(j){}...
};

上述代码意味,j用val赋值,然后i用j赋值,但这里存在一个陷阱,就是i先被声明,根据规则,i先初始化,但是此时j并没有被初始化过,所以i的值不确定,造成一个严重错误

5. c++类对象的大小

一个实例引出的思考

 

class X{};
class Y:virtual public X{};
class Z:virtual public X{};
class A:public Y, public Z{};

猜猜sizeof上面各个类都为多少?
Lippman的一个法国读者的结果是:

 

sizeof X yielded 1                         
sizeof Y yielded 8                         
sizeof Z yielded 8                         
sizeof A yielded 12                    

在vs2010上的结果是:

 

sizeof X yielded 1   
sizeof Y yielded 4   
sizeof Z yielded 4    
sizeof Z yielded 8        

当我们对于C++对象的内存布局知之甚少的情况下,想搞清这些奇怪现象的缘由将是一件非常困难的事情。
事实上,对于像X这样的一个的空类,编译器会对其动点手脚——隐晦的插入一个字节。为什么要这样做呢?插入了这一个字节,那么X的每一个对象都将有一个独一无二的地址。如果不插入这一个字节呢?哼哼,那对X的对象取地址的结果是什么?两个不同的X对象间地址的比较怎么办?
我们再来看Y和Z。首先我们要明白的是实现虚继承,将要带来一些额外的负担——额外需要一个某种形式的指针。到目前为止,对于一个32位的机器来说Y、Z的大小应该为5,而不是8或者4。我们需要再考虑两点因素:内存对齐(alignment)编译器的优化
那么在vs2010中为什么Y、Z的大小是4而不是8呢?我们先思考一个问题,X之所以被插入1字节是因为本身为空,需要这一个字节为其在内存中给它占领一个独一无二的地址。但是当这一字节被继承到Y、Z后呢?它已经完全失去了它存在的意义,为什么?因为Y、Z各自拥有一个虚基类指针,它们的大小不是0。既然这一字节在Y、Z中毫无意义,那么就没必要留着。也就是说vs2010对它们进行了优化,优化的结果是去掉了那一个字节。
当我们现在再来看A的时候,一切就不是问题了。对于那位Lippman的法国读者来说,A的大小是共享的X实体1字节,X和Y的大小分别减去虚基类带来的内存空间,都是4。A的总计大小为9,对齐以后就是12了。而对于vs2010来说,那个一字节被优化后,A的大小为8,也不需再进行alignment操作。

总结
影响C++类的大小的三个因素:

  • 支持特殊功能所带来的额外负担(对各种virtual的支持)。
  • 编译器对特殊情况的优化处理。
  • alignment操作,即内存对齐。

关于更多的memory alignment(内存对齐)的知识见VC内存对齐准则(Memory alignment)

关于pragma
#pragma pack(4) 可以指定对齐大小为4,另外还要满足以下规则
这里有一个小问题:vs对预处理没有进行语法检测,括号换为中文的不会报错,但也没有意义。
在结构体内部对齐大小是 min(pragma, 自身大小)
整个结构体对齐大小是 min(pragma, 最大数据成员大小)

6. c++对象的数据成员

数据成员的布局
对于一个类来说它的对象中只存放非静态的数据成员,但是除此之外,编译器为了实现virtual功能还会合成一些其它成员插入到对象中。我们来看看这些成员的布局。

C++ 标准的规定

  • 在同一个Access Section(也就是private,public,protected片段)中,要求较晚出现的数据成员处在较大的内存中。这意味着同一个片段中的数据成员并不需要紧密相连,编译器所做的成员对齐就是一个例子。
  • 允许编译器将多个Acess Section的顺序自由排列,而不必在乎它们的声明 次序。但似乎没有编译器这样做。
  • 对于继承类,C++标准并未指定是其基类成员在前还是自己的成员在前。
  • 对于虚基类成员也是同样的未予规定。

一般的编译器怎么做?

  • 同一个Access Section中的数据成员按期声明顺序,依次排列。但成员与成员之间因为内存对齐的原因可能存在空当。
  • 多个Access Section按其声明顺序排放。
  • 基类的数据成员总放在自己的数据成员之前,但虚基类除外。

编译器合成的成员放在哪?
为了实现虚函数和虚拟继承两个功能,编译器一般会合成Vptr和Vbptr两个指针。那么这两个指针应该放在什么位置?C++标准肯定是不曾规定的,因为它甚至并没有规定如何来实现这两个功能,因此就语言层面来看是不存在这两个指针的。

  • 对于Vptr来说有的编译器将它放在末尾,如Lippman领导开发的Cfront。有的则将其放在最前面,如MS的VC,但似乎没人将它放在中间。为什么不放在中间?没有理由可以让人这么做,放在末尾,可以保持C++类对C的struct的良好兼容性,放在最前可以给多重继承下的指针或引用调用虚函数带来好处。
  • 对于Vbptr来说,有好几种方法,在这儿我们只看看VC的实现原理:
    对于由虚拟继承而得的类,VC会在其每一个对象中插入一个Vbptr,这个Vbptr指向vitual base class table(我称之为虚基类表)。虚基类表中则存放有其虚基类子对象相对于虚基类指针的偏移量。例如声明如class Y:virtual public X的类的virtual base class table的虚基类表中当存储有X对象相对于Vbptr的偏移量。

对象成员或基类对象成员后面的填充空白不能为其它成员所用
如果有填充空白被使用,设想一下,将会造成数据错误,地址内存的值并不是你原本需要的值;

Vptr与Vbptr

  • 在多继承情况下,即使是多虚拟继承,继承而得的类只需维护一个Vbptr;而多继承情况下Vptr则可能有要维护多个Vptr,视其基类有几个有虚函数。
  • 一条继承线路只有一个Vptr,但可能有多个Vbptr,视有几次虚拟继承而定。换言之,对于一个继承类对象来说,不需要新合成vptr,而是使用其基类子对象的vptr。而对于一个虚拟继承类来说,必须新合成一个自己的Vbptr。
    如:

 

class X{virtual void vf(){};
};
class X2:virtual public X
{virtual void vf(){};
};
class X3:virtual public  X2
{virtual void vf(){};
}

X3将包含有一个Vptr,两个Vbptr。确切的说这两个Vbptr一个属于X3,一个属于X3的子对象X2,X3通过其Vbptr找到子对象X2,而X2通过其Vbptr找到X。
其中差别在于vptr通过一个虚函数表可以确切地知道要调用的函数,而Vbptr通过虚基类表只能够知道其虚基类子对象的偏移量。这两条规则是由虚函数与虚拟继承的实现方式,以及受它们的存取方式和复制控制的要求决定的。

数据成员的存取

静态数据成员相当于一个仅对该类可见的全局变量,因为程序中只存在一个静态数据成员的实例,所以其地址在编译时就已经被决定。不论如何静态数据成员的存取不会带来任何额外负担。
非静态数据成员的存取,相当于对象起始地址加上偏移量。效率上与C struct成员的效率等同。因为它的偏移量在编译阶段已经确定。但有一种情况例外:pt->x=0.0。当通过指针或引用来存取——x,而x又是虚基类的成员的时候。因为必须要等到执行期才能知道pt指向的确切类型,所以必须通过一个间接导引才能完成。

小结

在VC中数据成员的布局顺序为:

  1. vptr部分(如果基类有,则继承基类的)
  2. vbptr (如果需要)
  3. 基类成员(按声明顺序)
  4. 自身数据成员
  5. 虚基类数据成员(按声明顺序)

7. C++ 成员函数调用

c++支持三种类型的成员函数,分别为static,nostatic,virtual。每一种调用方式都不尽相同。

非静态成员函数(Nonstatic Member Functions)
保证nostatic member function至少必须和一般的nonmember function有相同的效率是C++的设计准则之一。事实上在c++中非静态成员函数(nostatic member function)与普通函数的调用也确实具有相同的效率,因为本质上非静态成员函数就如同一个普通函数,如一个非静态成员函数Xfloat Point::X();就相当于一个普通函数float X(Point* this);。编译器内部会将成员函数等价转换为非成员函数,具体是这样做的:

1.改写成员函数的签名,使得其可以接受一个额外参数,这个额外参数即是this指针:

 

float Point::X();
//成员函数X被插入额外参数this
float Point:: X(Point* this );

当然如果成员函数是const的,插入的参数类型将为 const Point 类型。

2.将每一个对非静态数据成员的操作都改写为经过this操作。

3.将成员函数写成一个外部函数,对函数名进行“mangling”处理,使之成为独一无二的名称。

  • 可以看出,将一个成员函数改写成一个外部函数的关键在于两点:
    一是给函数提供一个可以直接读写成员数据的通道;
    二是解决好有可能带来的名字冲突。
    第一点通过给函数提供一个额外的指针参数来解决,第二点则是通过一定的规则将名字转换,使之独一无二。
  • 由此可以做出一点总结:一个成员函数实际上就是一个被插入了一个接受其类的指针类型的额外参数的非成员函数,
    当然还要额外对函数的名称进行处理。额外插入的参数用来访问数据成员,而名称的特殊处理用来避免名字冲突。
  • 对于名称的特殊处理并没有统一的标准,各大编译器厂商可能有不同的处理规则。
    在VC下上述的成员函数X()的名称X处理后就成了?X@Point@@QAEMXZ
    更多信息可以参见维基百科的Visual C++名字修饰。

于是在VC中对于上面的例子中的成员函数的调用将发生如下的转换:

 

>//p->X();被转化为
point_X(Point *this);
//obj.X();被转化为
point_X(Point &obj);

虚拟成员函数(Virtual Member Functions)

如果function()是一个虚拟函数,那么用指针或引用进行的调用将发生一点特别的转换——一个中间层被引入进来。例如:

 

// ptr->function()
//将转化为
(*ptr->vptr[1])(ptr);
  • 其中vptr为指向虚函数表的指针,它由编译器产生。vptr也要进行名字处理,因为一个继承体系可能有多个vptr。
  • 1是虚函数在虚函数表中的索引,通过它关联到虚函数function().
  • 第二个ptr表示this指针

何时发生这种转换?答案是在必需的时候——一个再熟悉不过的答案。当通过指针调用的时候,要调用的函数实体无法在编译期决定,必需待到执行期才能获得,所以上面引入一个间接层的转换必不可少。但是当我们通过对象(不是引用,也不是指针)来调用的时候,进行上面的转换就显得多余了,因为在编译器要调用的函数实体已经被决定。此时调用发生的转换,与一个非静态成员函数(Nonstatic Member Functions)调用发生的转换一致。

静态成员函数(Static Member Functions)

静态成员函数的一些特性:

  1. 没有this指针,
  2. 不能够直接存取其类中的非静态成员(nostatic members),包括不能调用非静态
    成员函数(Nonstatic Member Functions)。
  3. 不能够声明为 const、voliatile或virtual。
  4. 它不需经由对象调用,当然,通过对象调用也被允许。
  5. 2、3、4主要因为1而产生

除了缺乏一个this指针他与非静态成员函数没有太大的差别。在这里通过对象调用和通过指针或引用调用,将被转化为同样的调用代码。

需要注意的是通过一个表达式或函数对静态成员函数进行调用,被C++ Standard要求对表达式进行求值。如:

 

(a+=b).static_fuc();

虽然省去对a+b求值对于static_fuc()的调用并没有影响,但是程序员肯定会认为表达式a+=b已经执行,一旦编译器为了效率省去了这一步,很难说会浪费多少程序员多少时间。这无疑是一个明智的规定。

C++ 之虚函数

《深度探索C++对象模型》是这样来说多态的:

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

消极多态与积极多态
用基类指针来寻址继承类的对象,我们可以这样:
Point ptr=new Point3d; //Point3d继承自Point
在这种情况下,多态可以在编译期完成(虚基类情况除外),因此被称作消极多态(没有进行虚函数的调用)。相对于消极多态,则有积极多态——指向的对象类型需要在执行期在能决定[^注1]积极多态的例子,如虚函数和RTTI:

 

//例1,虚函数的调用
ptr->z();
//例2,RTTI 的应用
if(Point3d *p=dynamic_cast<Point3d*>(ptr) )return p->z();

关于RTTI的笔记可见笔记EH & RTTI。本文主要精力将集中于虚函数上。对于一个如上例关于虚函数的调用,要如何来保证在执行期调用的是正确的z()实体——Point3d::z()而不是调用了Point::z()。来看看虚函数的实现机制吧,它将保证这一点。

单继承下的虚函数

虚函数的实现:

  • 为每个有虚函数的类配一张虚函数表,它存储该类类型信息和所有虚函数执行期的地址。
  • 为每个有虚函数的类插入一个指针(vptr),这个指针指向该类的虚函数表。
  • 给每一个虚函数指派一个在表中的索引。

用这种模型来实现虚函数得益于在C++中,虚函数的地址在编译期是可知的,而且这一地址是固定不变的。而且表的大小不会在执行期增大或减小。

一个类的虚函数表中存储有类型信息(存储在索引为0的位置)和所有虚函数地址,这些虚函数地址包括三种:

  • 这个类定义的虚函数,会改写(overriding)一个可能存在的基类的虚函数实体——假如基类也定义有这个虚函数。
  • 继承自基类的虚函数实体,——基类定义有,而这个类却没有定义。直接继承之。
  • 一个纯虚函数实体。用来在虚函数表中占座,有时候也可以当做执行期异常处理函数。

每一个虚函数都被指派一个固定的索引值,这个索引值在整个继承体系中保持前后关联,例如,假如z()在Point虚函数表中的索引值为2,那么在Point3d虚函数表中的索引值也为2。

当一个类单继承自有虚函数的基类的时候,将按如下步骤构建虚函数表:

  1. 继承基类中声明的虚函数——这些虚函数的实体地址被拷贝到继承类中的虚函数表中对于的slo中。
  2. 如果有改写(override)基类的虚函数,那么在1中应将改写(override)的函数实体的地址放入对应的slot中而不是拷贝基类的。
  3. 如果有定义新的虚函数,那么将虚函数表扩大一个slot以存放新的函数实体地址。

我们假设z()函数在Point虚函数表中的索引为4,回到最初的问题——要如何来保证在执行期调用的是正确的z()实体?其中微妙在于,编译将做一个小小的转换:

 

ptr->z();
//被编译器转化为:
(*ptr->vptr[4])(ptr);

这个转换保证了调用到正确的实体,因为:

  • 虽然我们不知道ptr所指的真正类型,但它可以通过vptr找到正确类型的虚函数表。
  • 在整个继承体系中z()的地址总是被放在slot 4。

多重继承下的虚函数

在多重继承下,继承类需要为每一条继承线路维护一个虚函数表(也有可能这些表被合成为一个,但本质意义并没有变化)。当然这一切都发生在需要的情况下。

当使用第一继承的基类指针来调用继承类的虚函数的时候,与单继承的情况没有什么异样,问题出生在当以第二或后继的基类指针(或引用)的使用上。例如:

 

//假设有这样的继承关系:class Derived:public base1,public base2;
//base1,base2都定义有虚析构函数。
base2 *ptr = new derived;
//需要被转换为,这个转换在编译期完成
base2 *ptr = temp ? temp + sizeof(base1) : 0 ;

如果不做出上面的转换,那么 ptr 指向的并不是 derived 的 base2 subobject
。后果是,ptr 将一个derived类型当做base2类型来用。

当要delete ptr时又面临了一次转换,因为在delete ptr的时候,需要对整个对象而不是其子对象施行delete运算符,这期间需要调整ptr指向完整的对象起点,因为不论是调用正确的析构函数还是delete运算符都需要一个指向对象起点的指针,想一想给予一个derived类的成员函数指向base2 subobjuect 的this指针会发生什么吧。因为ptr的具体类型并不知道,所以必须要等到执行期来完成。

Bjame的解决方法是将每一个虚函数表的slot
扩展,以使之存放一个额外的偏移量。于是虚函数的调用:

 

(*ptr->vptr[1])(ptr);
//将变成:
(*ptr->vptr[1].addr)(ptr+*ptr->vptr[1].offset);

其中使用ptr->vptr[1].addr用以获取正确的虚函数地址,而ptr+*ptr->vptr[1].offset来获得指向对象完整的起点。这种方法的缺点显而易见,代价过大了一点,所有的情况都被这一种占比较小的情况拖累。

还有一种叫做thunk的方法,thunk的作用在于:

  1. 以适当的offset值来this调整指针.
  2. 跳到虚函数中去。

Thunk技术即是:虚函数表中的slot仍然继续放一个虚函数实体地址,但是如果调用这个虚函数需要进行this调整的话,该slot中的地址就指向一个Thunk而不是一个虚函数实体的地址。

书中纷杂的讲到不少中种情况,但我以我的理解,做如下小结:

多继承下的虚函数,影响到虚函数的调用的实际质上为this的调整。而this调整一般为两种:

  1. 调整指针指向对应的subobject,一般发生在继承类类型指针向基类类型指针赋值的情况下。
  2. 将指向subobject的指针调整回继承类对象的起始点,一般发生在基类指针对继承类虚函数进行调用的时候。

第一点,使得该基类指针指向一个与其指针类型匹配的子对象,唯有如此才能保证使得该指针在执行与其指针类型相匹配的特定行为的正确性。比方调用基类的成员,获得正确的虚函数地址。可以想象如果不调整,用ptr存取base2 subobject的数据成员时,会发生什么?调用base2的成员函数的时候,其成员函数接受的this指针指向derived
类型对象,这又会发生什么?结果是整个对象的内存结构有可能都被破坏。还有别忘了,vptr也可以看做一个数据成员,要找到虚函数,前提是获取正确的vptr偏移量。

而第二点,显然是让一个继承类的虚函数获取一个正确的this指针,因为一个继承类虚函数要的是一个指向继承类对象的this指针,而不是指向其子对象。

第一顺序继承类之所以不需要进行调整的关键在于,其subobject的起点与继承类对象的起点一致。

虚拟继承下的虚函数

Lippman说,如果一个虚基类派生自另一虚基类,而且它们都支持虚函数和非静态数据成员的时候,编译器对虚基类的支持就像迷宫一样复杂。其实我原想告诉他,我是怀着一颗勇士之心而来的

你说呢?

你说呢?。

 

虽然书中没有介绍太多,但不难猜测的是在虚继承情况下,复杂点在仍旧在于this指针的调整,然而其复杂度显然又在多继承之上,因为又多了一个vbptr了。

构造、解构、拷贝 语意学(Semantics of Construction,Destruction,and Copy)

几点类设计原则

1.即使是一个抽象基类,如果它有非静态数据成员,也应该给它提供一个带参数的构造函数,来初始化它的数据成员。或许你可以通过其派生类来初始化它的数据成员(假如nostatic data member为publish或protected),但这样做的后果则是破坏了数据的封装性,使类的维护和修改更加困难。由此引申,类的data member应当被初始化,且只在其构造函数或其member function中初始化。

2.不要将析构函数设计为纯虚的,这不是一个好的设计。将析构函数设计为纯虚函数意味着,即使纯虚函数在语法上允许我们只声明而不定义纯虚函数,但还是必须实现该纯虚析构函数,否则它所有的继承类都将遇到链接错误。一个不能派生继承类的抽象类有什么存在的意义?必须定义纯虚析构函数,而不能仅仅声明它的原因在于:每一个继承类的析构函数会被编译器加以扩展,以静态调用方式其每一个基类的析构函数(假如有的话,不论是显示的还是编译器合成的),所以只要任何一个基类的析构函数缺乏定义,就会导致链接失败。矛盾就在这里,纯虚函数的语法,允许只声明而不定义纯虚析构函数,而编译器则死脑筋的看到一个其基类的析构函数声明,则去调用它的实体,而不管它有没有被定义。

3.真的必要的时候才使用虚函数,不要滥用虚函数。虚函数意味着不小的成本,编译很可能给你的类带来膨胀效应:

  • 每一个对象要多负担一个word的vptr。
  • 给每一个构造函数(不论是显示的还是编译器合成的),插入一些代码来初始化vptr,这些代码必须被放在所有基类构造函数的调用之后,但需在任意用户代码之前。没有构造函数则需要合成,并插入代码。
  • 合成一个拷贝构造函数和一个复制操作符(如果没有的话),并插入对vptr的初始化代码,有的话也需要插入vptr的初始化代码。
  • 意味着,如果具有bitwise语意,将不再具有,然后是变大的对象、没有那么高效的构造函数,没有那么高效的复制控制。

4.不能决定一个虚函数是否需要 const ,那么就不要它。

5.决不在构造函数或析构函数中使用虚函数机制。在构造函数中,每次调用虚函数会被决议为当前构造函数所对应类的虚函数实体,虚函数机制并不起作用。当一个base类的构造函数含有对虚函数vf()的调用,当其派生类derived的构造函数调用基类base的构造函数的时候,其中调用的虚函数vf()是base中的实体,而不是derived中的实体。这是由vptr初始化的位置决定的——在所有基类构造函数用之后,在程序员供应的代码或是成员初始化队列之前。因构造函数的调用顺序是:有根源到末端,由内而外,所以对象的构造过程可以看成是,从构建一个最基础的对象开始,一步步构建成一个目标对象。析构函数则有着与构造相反的顺序,因此在构造或析构函数中使用虚函数机制,往往不是程序员的意图。若要在构造函数或析构函数中调用虚函数,应当直接以静态方式调用,而不要通过虚函数机制。

构造、复制、析构语意学

一种所谓的Plain OI’Data声明形式:

 

struct Point {float x,y,z;
};

概念上来讲,对于一段这样的C++代码,编译器会为之合成一个默认构造函数、复制构造函数、析构函数、赋值操作符。然而实际上编译器会分析这段代码,并给Point贴上Plain OI’Data标签。编译器在此后对于Point的处理与在C中完全一样,也就是说上述的函数都不会被合成。可见概念上应当由编译器
合成的函数,并不一定会合成,编译器只有在必要的时候才会合成它们。由此一来,原本在观念上应该调用这些函数的地方实质上不会调用,而是用其它的方法来完成上面的功能,比方复制控制会用bitwise copy。

对象构造语意学

无继承情况下的对象构造:略。

单继承体系下的对象构造

对于简单定义的一个对象T object;,很明显它的默认构造函数会被调用(被编译器合成的或用户提供的)。但是一个构造函数究竟做了什么,就显得比较复杂了——编译器给了它很多的隐藏代码。编译器一般会做如下扩充操作

  1. 调用所有虚基类的构造函数,从左到右,从最深到最浅:
    • 如果该类被列于成员初始化列表中,任何明确明确指定的参数,都应 该被传递过来。若没有列入成员初始化列表中,虚基类的一个默认构造函数被调用(有的话)。
    • 此外,要保证虚基类的偏移量在执行期可存取,对于使用vbptr来实现虚基类的编译器来说,满足这点要求就是对vbptr的初始化。
    • 然而,只有在类对象代表着“most-derived class”时,这些构造函数才可能会被调用。一些支持这个行为的代码会被放进去(直观点说就是,虚基类的构造由最外层类控制)。
  2. 调用所有基类构造函数,依声明顺序:
    • 如果该基类被列入了成员初始化队列,那么所有明确指定的参数,应该被传递过来。
    • 没有列入的话,那么调用其默认构造函数,如果有的话。
    • 如果该基类是第二顺位或之后的基类,this 指针必须被调整。
  3. 正确初始化vptr,如果有的话。
  4. 调用没有出现在初始化成员列表中的member object的默认构造函数,如果有的话。
  5. 记录在成员初始化队列中的数据成员初始化操作以声明的顺序被放进构造函数中。

虚拟继承下的构造抑制

有如下继承体系:

image_thumb.png


根据c++ 语法,Point 的初始化应有most-derived class来施行。也就是说当Vertex3d为most-derived class的时候,应当由它的构造函数来调用Point的构造函数初始化Point子对象,Vertex3d的子对象的构造函数对于Point的调用则应当抑制。如果没有抑制会怎么样?当我们定义Vertex3d cv;时,Vertex3d的构造函数中调用Point的构造函数、而随之调用它的子对象,Point3d和Vertex的构造函数中也调用了Point的构造函数。先不说,对于同一个子对象进行三次初始化是否有效率,更重要的是,这将不可避免的带来错误。由Vertex3d指定的子对象Point的值,会被覆盖掉。

 

编译器通常使用一个条件变量来表示是否为most-derived class,各构造函数根据这个条件变量来决定是否调用虚基类的构造函数,因此通过控制这个条件变量,就可以抑制非most-derived class调用虚基类的构造函数。当然也有其它的方法来做同样的事。

对象复制语意学

设计一个类,并考虑到要以一个对象指定给另一个对象时,有三种选择:

  • 什么都不做,采用编译器提供默认行为(bitwise copy或者由编译器合成一个)。
  • 自己提供一个赋值运算符操作。
  • 明确拒绝将一个对象指定给另一个对象。

对于第三点,只要将赋值操作符声明为private,且不定义它就可以了。对于第二点,只有在第一点的行为不安全或不正确,或你特别想往其中插入点东西的时候。

以下四种情况 copy assignment operator(还是用它的英文名,感觉顺畅点),不具有bitwise copy语意,也就是说这些情况下,编译器要合成copy assignment operator而不能依靠bitwise copy来完成赋值操作,这四种情况与构造函数、
拷贝构造函数的情况类似,原因可以参考它们的。四种情况如下:

  • 类包含有定义了copy assignment operator的class object成员。
  • 类的基类有copy assignment operator。
  • 类声明有任何虚函数的时候(问题同样会出现在由继承类对象向基类对象拷贝的时候)。
  • 当class继承体系中有虚基类时。

在虚拟继承情况下,copy assignment opertator会遇到一个不可避免的问题,virtual base class subobject的复制行为会发生多次,与前面说到的在虚拟继承情况下虚基类被构造多次是一个意思,不同的是在这里不能抑制非most-derived class 对virtual base class 的赋值行为。

安全的做法是把虚基类的赋值放在最后,避免被覆盖。

对象析构语意学

只有在基类拥有析构函数,或者object member拥有析构函数的时候,编译器才为类
合成析构函数,否则都被视为不需要。
析构的顺序正好与构造相反:

  • 本身的析构函数被执行。
  • 以声明的相反顺序调用member object 的析构函数,如果有的话。
  • 重设vptr 指向适当的基类的虚函数表,如果有的话。
  • 以声明相反的顺序调用上一层的析构函数,如果有的话。
  • 如果当前类是 most-derived class,那么以构造的相反顺序调用虚基类的析构函数。

“在此之前”的叙述并不适合我,我喜欢很直白的方式,按顺序来。书中的方式在于,从最浅显的步骤入手,然后告诉你,做这步之前,你还该做点什么。

所以,我以对原文的理解写下这点。Lippman的原文为:

These constructors, however, may be invoked if, and only if, the class object represents the “most-derived class.” Some mechanism supporting this must be put into place.

侯捷的译文为:

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

我认为,Lippman在这一句上要说的是,虚基类的构造函数只能由most-derived class调用,而为了支持这一机制,需要插入一些代码来抑制非most-derived class对虚基类构造函数的调用。同时说一点,5.4的标题个人以为应该译为“对象的效率”而非“对象的功能”——原标题为:Object Efficency。

对象的构造和析构

一般而言,构造函数被安插在对象的定义处,而析构函数被安插在对象生命周期结束前:

 

// Pseudo C++ Code 
{ Point point; // point.Point::Point() 一般被安插在这儿 ... // point.Point::~Point() 一般被安插在这儿 
}

当代码有一个以上的离开点的时候,析构函数则必须放在对象被构造之后的每一个离开点之前。因此,尽可能将对象定义在接近要使用的地方,可以减少不必要的构造对象和析构对象的代码被插入到自己的代码当中。

全局对象

一个全局对象,c++保证它在main()在第一次使用它之前将其构造,而在main()结束之前,将之析构掉。C规定一个全局对象只能被一个常量表达式(编译期可知)赋初值。而构造函数显然不是一个常量表达式。虽然全局对象在编译期被即被置为0,但真正的构造工作却需要直到程序激活后才能进行,而
这个过程就是所谓的静态初始化。我是这样理解,但我不保证正确,因为全局变量,被放在data segment (数据段),data segment是在编译期已经布置好的,但构造函数的结果在编译期不能评估,因此先将对象的内容设置为0,存储在数据段,而等到程序激活时,这时候就可以通过构造函数对在数据段的全局对象进行初始化了,而这就是所谓的静态初始化。

静态初始化的对象有一些缺点:如果构造函数支持异常机制,那么遗憾的是对象的构造函数的调用,无法被放置与try块中,我们知道一个没有得到catch的异常默认的调用terminate()函数。也就是说一个全局对象在构造过程中抛出异常,将导致程序的终结,而更悲剧的是,你还无法来捕获并处理这个异常。另一点在于,在不同文件中定义的全局变量,构造顺序有规则吗?我不知道。即使有规则,如果不同的构造顺序对程序有影响的话,那么有多琐碎复杂…

Lippman甚至建议:根本就不要使用那些需要静态初始化的全局对象。真的非要一个全局对象,而且这个对象还需要静态初始化?那么我的方法是,用一个函数封装一个静态局部对象,也是一样的效果嘛。

局部静态对象(Local Static Object)

下面一段代码:

 

const Matrix&  identity()
{  static Matrix mat_identity;  // ...  return mat_identity;  
}

因为静态语意保证了 mat_identity 在整个程序周期都存在,而不会在函数
identity()退出时被析构,所以:

  • mat_identity的构造函数只能被施行一次,虽然identity()可以被调用多次。
  • mat_identity 的析构函数只能被施行一次,虽然identity()可以被调用多次。

那么 mat_identity的构造函数和析构函数到底在什么时候被调用?答案是:
mat_identity的构造函数只有在第一次被调用时在被施行,而在整个程序退出
之时按构造相反的顺序析构局部静态对象。

对象数组(Array of Objects)

对于定义一个普通的数组,例如:

 

Point knots[ 10 ];

实际上背后做的工作则是:

  1. 分配充足的内存以存储10个Point元素;
  2. 为每个Point元素调用它们的默认构造函数(如果有的话,且不论是合成的还是显式定义的)。编译器一般以一个或多个函数来完成这个任务。当数组的生命周期结束的时候,则要逐一调用析构函数,然后回收内存,编译器同样一个或多个函数来完成任务。这些函数完成什么功能,大概都能猜得出来。而关于细节,不必要死扣了,每个编译器肯定都有些许差别。

模板二事

模板的实例化

一个模板只有被使用到,才会被实例化,否则不会被实例化。对于一个实例化后的模板来说,未被调用的成员函数将不会被实例化,只有成员函数被使用时,C++标准才要求实例化他们。其原因,有两点:

  • 空间和时间效率的考虑,如果模板类中有100个成员函数,对某个特定类型只有2个函数会被使用,针对另一个特定类型只会使用3个,那么如果将剩余的195个函数实例化将浪费大量的时间和空间。
  • 使模板有最大的适用性。并不是实例化出来的每个类型都支持所有模板的全部成员函数所需要的运算符。如果只实例化那些真正被使用的成员函数的话,那么原本在编译期有错误的类型也能够得到支持。
    可以明确的要求在一个文件中将整个类模板实例化:
    template class Point3d<float>;
    也可以显示指定实例化一个模板类的成员函数:
    template float Point3d<float>::X() const;
    或是针对一个模板函数:

 

template Point3d<float> operator+(const Point3d<float>&, const Point3d<float>& );

模板的错误报告,使用模板并遇到错误的大概都深有体会,那就是一个灾难。

模板的名称决议

一开始先要区分两种意义,一种是C++ 标准所谓的“scope of the templatedefinition”,直译就是“定义模板的范围”。另一种是C++标准所谓的“scope ofthe temlate instantiation”,可以直译为“实例化模板的范围”。
第一种情况:

 

// scope of the template definition
extern double foo ( double ); template < class type > 
class ScopeRules 
{ 
public: void invariant() { _member = foo( _val ); } type type_dependent() { return foo( _member ); } // ... 
private: int _val; type _member; 
};

第二种情况:

 

//scope of the template instantiation 
extern int foo( int ); 
// ... 
ScopeRules< int > sr0; 
sr0.invariant();
sr0.type_dependent();

在“scope of the template instantiation ”中 两个foo()都声明在此 scope中。猜猜sr0.invariant() 中调用的是哪个foo()函数,出乎意料,实际调用的是:

extern double foo ( double );

看上去,应该调用:
extern int foo( int );

毕竟,_val 的类型是 int 类型,它们才完全匹配。而 sr0.type_dependent()中调用的却在我们意料之中,调用的是:
extern int foo( int );

诸上所述,看上去或合理或不合理的选择,原因在于:

template 之中, 对于一个非成员名字的决议结果是根据这个 name的使用是否与“用以实例化该模板的参数类型”有关来决定name。如果其使用互不相干,那么就以“scope of the template dclaration”来决定name。如果其使用的互相关联,那么就以“scope of the template instantiation”来决定name.

对于上面这一段话我的理解比较粗鲁且直接:在模板中,一个非成员名字的决议在于它适不适合在当前决议,当它完全与实例化模板的参数的类型无关的时候,就可以在当前决议下来;如果有关的话,则认为不适合在当前决议下来,将被推迟到实例化这个模板实例化的时候来决议。为什么以与实例化的类型相关不相关来区别适不适合当前决议?一个与实例化类型无关的名字,如果推迟到实例化的时候来决议,将使模板的设计者无所适从,一个模板的设计者能容忍一个与实例化类型无关的名字在他的模板中表现出当前不具有的含义吗?当然不行,那种场面,估计没有一个模板设计者能够hold住。相反,对于一个与实例化类型有关的名字,天生就应该可以根据实例化模板的不同类型表现出不同含义,如果其名字早在模板定义时被决议出来,那就该轮到模板的使用者hold不住了。当然所上完全属一家之言,呸,连一家之言都不算,怎么敢自称“家”。如有不同理解,可当我一派胡言,如果你聊发善心,可以对我赐教一二,当聆听受教。

异常处理(Exception Handling)

C++的 exception handling 有三个主要的子句组成:

  • 一个throw子句。它在程序的某处丢出一个exception,被丢出的exception可以是内建类型,也可以是自定义类型。——抛出exception组件。
  • 一个或多个 catch 子句。 每一个 catch 子句都是一个 exception handler。每个子句可以处理一种类型(也包括其继承类)的exception,在大括号中包含处理代码。——专治各种不服组件。每一个catch子句都可以用来处理某种exception。
  • 一个 try 区段。用大括号包围一系列语句,这些语句有可能抛出exception,从而引发catch 子句的作用。——逮捕各种 exception 组件。

当一个 exception 被抛出后,控制权从函数调用中被释放,寻找一个吻合的catch子句,如果各层调用都没有吻合的catch子句,terminate()将被调用。在控制权被放弃后,堆栈中的每一个函数调用也被出栈,这个过程称为unwinding the stack(关于 stack unwinding ,可以参考《C++ Primer》第四版之 17.1.2 Stack Unwinding),在每一个函数被出栈之前,其局部变量会被摧毁。

异常抛出有可能带来一些问题,比方在一块内存的lock和unlock内存之间,或是在new和delete之间的代码抛出了异常,那么将导致本该进行的unlock或delete操作不能进行。解决方法之一是:

 

void  mumble( void *arena ) 
{ Point *p; p = new Point; try { smLock( arena ); // ... } catch ( ... ) { smUnLock( arena ); delete p; throw; } smUnLock( arena ); delete p; 
}

在函数被出栈之前,先截住异常,在unlock和delete之后再将异常原样抛出。new expression的调用不用包括在try块之内是因为,不论在new operator调用时还是构造函数调用时抛出异常,都会在抛出异常之前释放已分配好的资源,所以不用再调用delete 。

另一个办法是,将这些资源管理的问题,封装在一个类对象中,由析构函数释放资源,这样就不需要对代码进行上面那样的处理——利用函数释放控制权之前会析构所有局部对象的原理。

在对单个对象构造过程中抛出异常,会只调用已经构造好的base class object或member classobject的析构函数。同样的道理,适用于数组身上,如果在调用构造函数过程中抛出异常,那么之前所有被构造好的元素的析构函数被调用,对于抛出异常的该元素,则遵循关于单个对象构造的原则,然后释放已经分配好的内存。

只有在一个catch子句评估完毕并且知道它不会再抛出exception后,真正的exception object才会被释放。关于 catch子句使用引用还是使用对象来捕获异常,省略。

执行期类型识别(Runtime Type Identification RTTI)

  1. RTTI 只支持多态类,也就是说没有定义虚函数是的类是不能进行 RTTI的。
  2. 对指针进行dynamic_cast失败会返回NULL ,而对引用的话,识别会抛出bad_cast exception
  3. typeid 可以返回const type_info&,用以获取类型信息。

关于1是因为RTTI的实现是通过vptr来获取存储在虚函数表中的type_info* ,事实上为非多态类提供RTTI,也没有多大意义。 2的原因在于指针可以被赋值为0,以表示 no object,但是引用不行。关于3,虽然第一点指出RTTI只支持多态类,但typeidtype_info同样可用于内建类型及所有非多态类。与多态类的差别在于,非多态类的type_info对象是静态取得(所以不能叫“执行期类型识别”),而多态类的是在执行期获得。

感谢原作者分享

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

相关文章

  1. 解决Navicat 连接 Mysql 8.0.11 出现1251- Client does not support的问题

    解决Navicat 连接 Mysql 8.0.11 出现1251- Client does not support的问题解决方案 解决方案 今天安装了一个破解版的mysql,然后尝试在Navicat中连接一下,结果报了个错1251,client does notsupport。。。有点懵,赶紧搜了一下,给出的说法是mysql8 之前的版本中加密规则是my…...

    2024/4/16 10:58:41
  2. Linux系统安全 挺重要噢!

    账号安全控制 用户账号,是计算机使用者的身份凭证或标识,每一个要访问系统资源的额人,必须凭借其用户账号才能进入计算机。在Linux系统中,提供了多种机制来确保用户账号的正当、安全使用。 基本安全措施 系统账号清理 将非登录用户的Shell设为/sbin/nologiin 在Linux系统中…...

    2024/4/21 12:48:40
  3. LINUX网络编程

    这是别人网络编程的文章,觉得不错,附上链接一、基础理论篇01、网络协议入门02、LAN、WAN、WLAN、VLAN 和 VPN 的区别03、IP 地址介绍04、广播地址介绍05、无连接和面向连接协议的区别06、因特网的IP协议是不可靠无连接的,那为何当初不直接把它设计为可靠?07、C/S和B/S架构的…...

    2024/4/19 8:47:18
  4. 填坑日志(20200611) Feign入参为Null引起的血案

    表现:接口偶然失败 某天我正开心的在GitHub上乱晃悠,测试妹子就丢了个Bug过来。 “这个接口请求偶尔会失败。”附了一张截图。 “OK,交给我”。看了下接口,里面就是个列表查询,顺手开了PostMan对着Idea开始怼之后就暂时没管了,心说过半小时再说。半个小时之后,发现并没有…...

    2024/4/16 11:00:03
  5. 【Android 电量优化】电量优化 ( 耗电量测试 | Battery Historian 简介 | apt 源更新 | Docker 安装 | Battery Historian 安装 )

    文章目录一、耗电量测试二、Battery Historian 简介三、Docker 简介四、更新 apt 源五、安装 Docker六、安装 Battery Historian七、访问电量分析界面一、耗电量测试电量消耗主要是手机硬件的电量消耗 , 如显示屏 , CPU , GPS 定位模块 , WIFI 模块 , 4G/5G 模块 , 启用某些硬件…...

    2024/4/16 10:58:26
  6. Linux 安装软件(pycharm)的步骤

    1.下载软件 https://www.jetbrains.com/pycharm/download/#section=linux linux的安装包是以.tar.gz格式。2.解压的命令: tar -zxvf 下载包名 3.修改文件的配置 vi /etc/hosts 新加一行数据: 0.0.0.0 account.jetbrains.com 保存时用:w !sudo tee % 4: 启动 cd /home/j…...

    2024/4/16 10:59:07
  7. C#学习笔记-CodeFirst的使用方法

    Code First(代码优先):模型类由自己来写,然后会自动生成数据库。相比于DataBaseFirst,虽然这个很方便和简洁,但不适用于大型项目开发。因此一般都推荐使用CodeFirst。 直接用一个练习题来演示CodeFirst的功能 1.新建控制台应用程序,并在菜单-工具-库程序包管理器-管理解…...

    2024/4/20 3:46:39
  8. ML&DL - TensorFlow2.1学习笔记

    ML&DL - TensorFlow2.1学习笔记第一讲 神经网络计算1.1 人工智能三学派1.2 神经网络设计过程梯度下降法梯度下降法的缺点1.3 张量 (Tensor)1. 张量2. 数据(张量)类型3. 张量生成1.4 TensorFlow2常用函数(1)强制转换数据类型,最大、最小值,平均值,求和axis参数:可训…...

    2024/4/16 10:58:52
  9. UDP的epoll并发框架-UDP Listener解决OpenUOM的并发问题

    写于2016/01/21 参加完小小转学深圳小白鸽幼儿园的入学家长会之后UDP具有是一种很好的封装协议,比如OpenUOM使用UDP封装会比TCP好很多,现在越来越多的业务采用UDP传输,然后自己定义按序到达以及流控逻辑,然而就我个人的使用经验来看,UDP太难做并发,大多数情况下,使用UDP…...

    2024/4/16 10:59:27
  10. 网络安全学习篇14_简单渗透测试

    上一篇博客:PKI声明 此博客为笔记类博客,为简单的渗透测试流程,旨在了解渗透原理,不涉及任何非法操作。目录 简单渗透测试流程授权 信息收集 扫描漏洞 漏洞利用 其他操作 渗透测试报告其他开始授权 开始渗透测试前,要先得到公司或个人服务器管理员的授权信息收集 nslookup…...

    2024/4/16 10:58:21
  11. 流式布局

    移动web开发流式布局 1.0 移动端基础 1.1浏览器现状 PC端常见浏览器:360浏览器、谷歌浏览器、火狐浏览器、QQ浏览器、百度浏览器、搜狗浏览器、IE浏览器。 移动端常见浏览器:UC浏览器,QQ浏览器,欧朋浏览器,百度手机浏览器,360安全浏览器,谷歌浏览器,搜狗手机浏览器,猎…...

    2024/4/15 18:02:15
  12. 阿里云域名cloudflare dns搭建trojan服务器

    搭建服务器教程参阅: 添加链接描述 下文假设你已经为aliyun域名申请了ssl证书,如果没有的话请联系aliyun客户,很专业的 如果使用阿里云申请的域名与ssl证书,运行trojan时ssl握手失败。 原因猜测是aliyun域名已经和aliyun上申请的ssl证书绑定导致acme申请的ssl证书无法使用。…...

    2024/4/16 11:00:03
  13. Qt Remote Object(QtRO)实现进程间通信——遥控小车(一)

    Q-Bus的官方例子是个遥控小车在windows上自己手动编译了dbus,但还是跑不起来,在搜索Qt进程通信方式的时候,无意中看到Qt Remote Object(QtRO),这种方式支持跨进程的信号与槽,就尝试着用QtRO来改造这个遥控小车,让它跑起来。 看看我们的运行效果:最后发现用QtRO其实更好…...

    2024/4/16 10:59:37
  14. 选择BI工具需注意什么

    BI不同于一般的企业管理软件,不能简单归类为类似用于提高管理的ERP和WMS,或用于提高企业效率的OA、BPM。BI的本质应该是通过展现数据,用于加强企业各环节的管控,帮助快速制定科学的决策。那如何在如此多的BI工具中选择最适合自己企业的呢?你可能需要从以下几个方面考虑。技…...

    2024/4/19 4:20:45
  15. ubuntu 18.04 删除桌面pycharm快捷方式

    打开终端,输入:/$ sudo rm -rf ‘/usr/share/applications/jetbrains-pycharm.desktop’. 如下:...

    2024/4/16 10:59:17
  16. 周幺成7.5.6现货黄金周线趋势分析原油黄金下周行情展望

    忙碌了一周,有人收获,有人却要收拾留下的残局,与其捶胸顿足不如好好反思一下自己在市场上失利的原因,只有不断 找出自身的不足才能够不断进步,没有人天生就是站在高峰上,每个人都是经历了无数的挫折才能够成功,想成为一个成功的投资者也是一样的道理,反思不足,虚心学习…...

    2024/4/16 10:58:31
  17. 基于jupyter-notebook的口罩数据集的划分、训练、测试,并实现摄像头口罩识别判断

    目录口罩数据集的划分、训练、测试下载数据集划分数据集构建小型卷积网络数据预处理训练数据增强实现摄像头口罩识别判断基于口罩数据集训练出的模型进行识别通过开源的数据模型进行识别下载运行 口罩数据集的划分、训练、测试 这里的方法类似与笑脸识别的方法,也是类似于猫狗…...

    2024/4/16 10:59:12
  18. CMOS级时序分析

    1、COMS静态管简介 金属-氧化物-半导体(Meatl-Oxide-Semiconductor)结构的晶体管简称MOS晶体管,有P型MOS管和N型MOS管之分。由MOS管构成的集成电路称MOS集成电路,而由PMOS管和NMOS管共同构成的互补型MOS集成电路即为CMOS-IC(Complementary MOS Integrated Circuit)。 MOS…...

    2024/4/16 10:59:37
  19. 一起Talk Android吧(第二百五十八回:Android中的Toolbar菜单一)

    各位看官们大家好,上一回中咱们说的是Android中Toolbar的例子,这一回咱们继续说该例子。闲话休提,言归正转。让我们一起Talk Android吧! 看官们,我们在上一回中介绍了Toolbar的标题,本章回中我们将介绍Toolbar的菜单。菜单位于Toolbar的最右侧,就是有三个点图标,点击后会…...

    2024/4/20 6:49:53
  20. 问题:android studio 无法识别编译出来的模拟器

    问题 android studio 无法识别编译出来的模拟器 用adb 是可一查看到设备的 ~$ adb devices -l List of devices attached emulator-5554 device product:aosp_car_x86_64 model:Car_on_x86_64_emulator device:generic_x86_64 transport_id:2adb kill-server adb star…...

    2024/4/18 22:24:39

最新文章

  1. Rust的Vec<T>

    Vec<T> 是 Rust 编程语言中用于表示动态大小的数组的数据结构。它提供了类似其他语言中动态数组&#xff08;如 C 的 std::vector 或 Python 的 list&#xff09;的功能&#xff0c;允许在运行时高效地添加、删除或修改元素&#xff0c;同时确保内存的有效管理。Vec<T…...

    2024/4/26 17:27:30
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. 最新在线工具箱网站系统源码

    内容目录 一、详细介绍二、效果展示1.部分代码2.效果图展示 三、学习资料下载 一、详细介绍 系统内置高达72种站长工具、开发工具、娱乐工具等功能。此系统支持本地调用API&#xff0c;同时还自带免费API接口&#xff0c; 是一个多功能性工具程序&#xff0c;支持后台管理、上…...

    2024/4/23 4:11:28
  4. 分享一个基于Multi-SLAM+3DGS的新一代三维内容生产技术

    基于智能空间计算&#xff0c;新一代超逼真三维内容生成技术。 可自动化生成超逼真的大场景三维模型&#xff0c;并在各类终端和空间计算设备中&#xff0c;实现前所未有的沉浸式体验。 更可接入专业三维软件和应用平台&#xff0c;进行深度的模型开发与场景落地。 支持超大复杂…...

    2024/4/25 1:55:56
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/4/25 11:51:20
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/4/25 18:39:24
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/25 18:38:39
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/4/25 18:39:23
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/25 18:39:22
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/25 18:39:22
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/25 18:39:20
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/25 16:48:44
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/26 16:00:35
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/4/25 18:39:16
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/25 18:39:16
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/25 0:00:17
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/25 4:19:21
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/25 18:39:14
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/4/25 18:39:12
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/4/25 2:10:52
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/25 18:39:00
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/25 13:19:01
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/4/25 18:38:58
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/25 18:38:57
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57