??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲香蕉网久久综合影视,久久99久久99精品免视看动漫,久久久国产亚洲精品http://www.shnenglu.com/zqsand/category/12848.html啥是子标?/description>zh-cnMon, 05 Apr 2010 09:07:03 GMTMon, 05 Apr 2010 09:07:03 GMT60关于寚whttp://www.shnenglu.com/zqsand/archive/2010/04/04/111589.htmlrikisandrikisandSun, 04 Apr 2010 08:00:00 GMThttp://www.shnenglu.com/zqsand/archive/2010/04/04/111589.htmlhttp://www.shnenglu.com/zqsand/comments/111589.htmlhttp://www.shnenglu.com/zqsand/archive/2010/04/04/111589.html#Feedback3http://www.shnenglu.com/zqsand/comments/commentRss/111589.htmlhttp://www.shnenglu.com/zqsand/services/trackbacks/111589.html

数据寚wQ是指数据所在的内存地址必须是该数据长度的整数倍。处理器可以直接讉K寚w的数据,而访问未寚w的数据会在内部进行一pd的调_虽然可以正常处理Q但是会降低q行速度。例如一个处理器ȝ宽度?4位,那么地址必须?的倍数Q也是一ơ取?个字节。如果我们可以保证所有double地址都ؓ8的倍数Q那么可以用一个存储器操作来读或者写double了,否则我们可能需要执行两ơ存储器讉KQ因为对象可能位于两?字节存储器块?/font>

对于l构体也是一L例如 struct s2{int i;int j;char c;}; 如果我们把这个结构打包成9个字节,只要保证起始地址满4的对齐要求我们就可以保证i j的对齐,不过如果我们声明了一个数l,那么元素地址成ؓ xd,xd+9,xd+18,xd+27 不满_齐了。因此我们呢要分?2个字节给q个l构?/font>

更进一?Q?/font>

未对齐的数据会以不同方式lcpu带来ȝ~

1.如果一个变量被划分C个缓存行Q那么我们需要访问两个缓存行才可以?/font>

2.一些simd指oL要求寚w的指令,对于未对齐的指oQ数据对齐也会媄响这些指令的使用?/font>



rikisand 2010-04-04 16:00 发表评论
]]>
USACO 4.2.2 W一道网l流····http://www.shnenglu.com/zqsand/archive/2010/03/26/110584.htmlrikisandrikisandFri, 26 Mar 2010 05:04:00 GMThttp://www.shnenglu.com/zqsand/archive/2010/03/26/110584.htmlhttp://www.shnenglu.com/zqsand/comments/110584.htmlhttp://www.shnenglu.com/zqsand/archive/2010/03/26/110584.html#Feedback0http://www.shnenglu.com/zqsand/comments/commentRss/110584.htmlhttp://www.shnenglu.com/zqsand/services/trackbacks/110584.htmlDrainage Ditches
Hal Burch

Every time it rains on Farmer John's fields, a pond forms over Bessie's favorite clover patch. This means that the clover is covered by water for awhile and takes quite a long time to regrow. Thus, Farmer John has built a set of drainage ditches so that Bessie's clover patch is never covered in water. Instead, the water is drained to a nearby stream. Being an ace engineer, Farmer John has also installed regulators at the beginning of each ditch, so he can control at what rate water flows into that ditch.

Farmer John knows not only how many gallons of water each ditch can transport per minute but also the exact layout of the ditches, which feed out of the pond and into each other and stream in a potentially complex network. Note however, that there can be more than one ditch between two intersections.

Given all this information, determine the maximum rate at which water can be transported out of the pond and into the stream. For any given ditch, water flows in only one direction, but there might be a way that water can flow in a circle.

PROGRAM NAME: ditch
INPUT FORMAT

Line 1:
Two space-separated integers, N (0 <= N <= 200) and M (2 <= M <= 200). N is the number of ditches that Farmer John has dug. M is the number of intersections points for those ditches. Intersection 1 is the pond. Intersection point M is the stream.

Line 2..N+1:
Each of N lines contains three integers, Si, Ei, and Ci. Si and Ei (1 <= Si, Ei <= M) designate the intersections between which this ditch flows. Water will flow through this ditch from Si to Ei. Ci (0 <= Ci <= 10,000,000) is the maximum rate at which water will flow through the ditch.

SAMPLE INPUT (file ditch.in)
5 4
1 2 40
1 4 20
2 4 20
2 3 30
3 4 10
OUTPUT FORMAT

One line with a single integer, the maximum rate at which water may emptied from the pond.

SAMPLE OUTPUT (file ditch.out)
50
最基本的网l流
   1:  #include<iostream>
   2:  #include<fstream>
   3:  #include<string>
   4:  #include<vector>
   5:  #include<map>
   6:  #include<algorithm>
   7:  #include<sstream>
   8:  #include <cstring>
   9:  #include <queue>
  10:  using namespace std;
  11:  const int MAXN = 220;
  12:  const int infi = 0x7FFFFFFF;
  13:   int capacity[MAXN][MAXN], prenode[MAXN], flow[MAXN];
  14:   queue<int> mq; 
  15:   
  16:  int start, end, N;
  17:  void init(){
  18:      freopen("ditch.in","r",stdin);
  19:      //freopen("e:\\usaco\\ditch.in","r",stdin);
  20:      start = 1;  
  21:      scanf("%d %d",&N,&end); int c, s, t;
  22:      memset(capacity,0,sizeof(capacity));
  23:      for(int i=0;i<N;i++)
  24:      {
  25:          scanf("%d %d %d",&c,&s,&t);
  26:          capacity[c][s] += t; //两个节点间不只有一条\
  27:      } 
  28:  }
  29:  int bfs(){//L增广路径
  30:      while(!mq.empty()) mq.pop(); 
  31:      mq.push(start);  //源节点入?/span>
  32:      //memset(flow,0,sizeof(flow));
  33:      memset(prenode,-1,sizeof(prenode)); //重置前向节点
  34:      prenode[start] = 0; flow[start]=infi; //源节Ҏ量无限大
  35:      while(!mq.empty()){
  36:          int cur = mq.front(); 
  37:          mq.pop();
  38:          if(cur == end) break; //到达汇点l束路径 
  39:          for(int i=1;i<=end;i++){ 
  40:              if(prenode[i]==-1 && capacity[cur][i]) //讉K当前节点所有未讉K的相邻节点,更新flow
  41:              {
  42:                  prenode[i] = cur;
  43:                  flow[i] = (flow[cur]<capacity[cur][i]?flow[cur]:capacity[cur][i]);
  44:                  mq.push(i);
  45:              }
  46:          }
  47:      }
  48:      if(prenode[end]==-1)  //如果未找到增q\径返?1
  49:          return -1;
  50:      return flow[end];
  51:  }
  52:  int Edmonds_Karp(){
  53:      int total = 0, pathcapacity;//pathcapacity 路径增加?/span>
  54:      while((pathcapacity = bfs()) != -1){//可以扑ֈ增广路径时候进行@?/span>
  55:          int cur = end;    //从汇点开始增加逆向节点
  56:          while( cur != start ){
  57:              int t = prenode[cur] ;
  58:              capacity[t][cur] -= pathcapacity;
  59:              capacity[cur][t] += pathcapacity;
  60:              cur = t;
  61:          }
  62:          total += pathcapacity;//max_flow
  63:      }
  64:      return total;
  65:  }
  66:  void output(){
  67:      freopen("ditch.out","w",stdout);
  68:      //freopen("c:\\usaco\\ditch.out","w",stdout);
  69:      cout<<Edmonds_Karp()<<endl;
  70:  } 
  71:     int main()
  72:  {
  73:      init();  
  74:      output();
  75:      return 0;
  76:  }

标程Q用贪心法Q寻找一条增q\径的时候不断寻找cap最大的Q未被访问的节点mlocQ然后更新跟mloc盔R的节点flow?/font>

及prenode信息.最后当q行到end时候,更新路径节点capacityQ同时增加max_flow.重复上述q程直到找不到增q\?/font>

   1:  #include <stdio.h>
   2:  #include <string.h>
   3:   
   4:  #define MAXI 200
   5:   
   6:  /* total drain amount between intersection points */
   7:  int drain[MAXI][MAXI];
   8:  int nint; /* number of intersection points */
   9:   
  10:  int cap[MAXI]; /* amount of flow that can get to each node */
  11:  int vis[MAXI]; /* has this node been visited by Dijkstra's yet? */
  12:  int src[MAXI]; /* the previous node on the path from the source to here */
  13:   
  14:  int augment(void)
  15:   { /* run a Dijkstra's varient to find maximum augmenting path */
  16:    int lv;
  17:    int mloc, max;
  18:    int t;
  19:   
  20:    memset(cap, 0, sizeof(cap));
  21:    memset(vis, 0, sizeof(vis));
  22:   
  23:    cap[0] = 2000000000;
  24:    while (1)
  25:     {
  26:      /* find maximum unvisited node */
  27:      max = 0;
  28:      mloc = -1;
  29:      for (lv = 0; lv < nint; lv++)
  30:        if (!vis[lv] && cap[lv] > max)
  31:         {
  32:          max = cap[lv];
  33:      mloc = lv;
  34:         }
  35:      if (mloc == -1) return 0;
  36:      if (mloc == nint-1) break; /* max is the goal, we're done */
  37:   
  38:      vis[mloc] = -1; /* mark as visited */
  39:   
  40:      /* update neighbors, if going through this node improves the
  41:         capacity */
  42:      for (lv = 0; lv < nint; lv++)
  43:        if (drain[mloc][lv] > cap[lv] && max > cap[lv])
  44:         {
  45:          cap[lv] = drain[mloc][lv];
  46:      if (cap[lv] > max) cap[lv] = max;
  47:      src[lv] = mloc;
  48:         }
  49:     }
  50:    max = cap[nint-1];
  51:   
  52:    /* augment path, starting at end */
  53:    for (lv = nint-1; lv > 0; lv = src[lv])
  54:     {
  55:      t = src[lv];
  56:      drain[t][lv] -= max;
  57:      drain[lv][t] += max;
  58:     }
  59:    return max;
  60:   }
  61:   
  62:  int main(int argc, char **argv)
  63:   {
  64:    FILE *fout, *fin;
  65:    int lv;
  66:    int num;
  67:    int p1, p2, c;
  68:   
  69:    if ((fin = fopen("ditch.in", "r")) == NULL)
  70:     {
  71:      perror ("fopen fin");
  72:      exit(1);
  73:     }
  74:    if ((fout = fopen("ditch.out", "w")) == NULL)
  75:     {
  76:      perror ("fopen fout");
  77:      exit(1);
  78:     }
  79:   
  80:    fscanf (fin, "%d %d", &num, &nint);
  81:    while (num--)
  82:     {
  83:      fscanf (fin, "%d %d %d", &p1, &p2, &c);
  84:      p1--;
  85:      p2--;
  86:      drain[p1][p2] += c; /* note += handles two ditches between same points */
  87:     }
  88:   
  89:    /* max flow algorithm: augment while you can */
  90:    c = 0;
  91:    while ((p1 = augment()))
  92:      c += p1;
  93:    fprintf (fout, "%d\n", c);
  94:    return 0;
  95:   }

 

 

 

 

 

 

 

 



rikisand 2010-03-26 13:04 发表评论
]]>
深入探烦C++对象模型MW记 (?http://www.shnenglu.com/zqsand/archive/2010/03/25/110552.htmlrikisandrikisandThu, 25 Mar 2010 13:09:00 GMThttp://www.shnenglu.com/zqsand/archive/2010/03/25/110552.htmlhttp://www.shnenglu.com/zqsand/comments/110552.htmlhttp://www.shnenglu.com/zqsand/archive/2010/03/25/110552.html#Feedback0http://www.shnenglu.com/zqsand/comments/commentRss/110552.htmlhttp://www.shnenglu.com/zqsand/services/trackbacks/110552.html最后一章~~拖了几天Q得赶紧C了~~

名字Q?On the cusp of the object model

7.1 Template

Template用的很少Q这节中的有些东西比较晦涩,挑一些能理解的记下吧。剩下的{用的多了再回头来看?/font>

Template的具现行?template instantiation

Template <calss T>class Point{

  public: enum Status{unallocated,normalized};Point(T x=0.0,T y=0.0);

~Point() ; void * operator new(size_t);private:static Point<T> *freelist

static int chunksize; T _x,_y;

};

~译器看到这L声明Q会有什么动作?Q?/font>

没有。static data 不可?enum 不可?Qenum虽然cd固定Q但他只能和template point class 的某个实体来存取或操作。我们可?point<float>::Status s;

但是不能 point::status s; 因此我们可能xenum抽离C个非模板cM避免多重拯。同样道理,freelist 和chunksize对于E序而言也不可用。必L指定point<float>::freelist.也就是说静态数据成员是和特定类型的cȝ定的而不是泛型的模板cR但是如果定义一个指针不一定会L出相应的c,因ؓ一个指针,q不一定指向一个class object。编译器不需要知道该class 相应的Q何member 或者数据或?/font>

内存布局Q所以没有必要具现。但是如果是定义q初始化一个引用就真的会具现出一个实体,因ؓ不存在空引用?/font>

成员函数q不应该被实体化Q只有当他被调用的时候才需要被L出来?/font>

template<class T>

class mumble{

public: Muble(T t=1024):tt(t){ if(tt!=t)throw ex; }

private: T tt;

};

上面的模板中出现错误有,t=1024 不一定成功,!= 不一定定?/font>

q两个错误只有到L操作l合具体cd才能定出来

~译器面对一个template声明Q在他被一l实际参数具C前,只能实行以有限地错误查,只有特定实体定义之后Q才会发C些与语法无关的但是十分明昄错误Q这是技术上的一大问题?/font>

Template 的名U决议方?/font>

.h文g定义

void out(int x){
    cout<<"out_by_class_define_scrope"<<endl;
}
template <class type>
class A{
public:
    void test1(){
        out(m1);
    }
    void test2(){
        out(m2);
    }
private:
    int m1;
    type m2;
};

.cpp文g定义

void out(double x){
    cout<<"out_by_class_instantiation_scrope"<<endl;
}

int main(Q?br>{    
    A<double> a;
    a.test1();
    a.test2();

}

按照书中的说法,如果一个函敎ͼ参数cd和type无关的话Q应该取他的scope of template declaration中定义的函数Q而反之取在他的instantiation中的那个。事实上在测试中发现

MSVC ?Q函数定义的册是依照类型的Q如果有合适的函数比如type是doubleQ此时如果定义处或者具现处有double型的函数定义Q那么函数就会决议ؓ那一个定义的~~~

MEMber function的具现行为:

~译器如何确保只有一个vtable产生Q?一U方法是 每一个virtual func地址都放在vtable中,如果取得函数地址Q则表示virtual func 的定义必然出现在E序的某个地点,否则E序无法q接成功。此外该函数只能有一个实体,否则也是q接不成功。那么,把vtable攑֜定义了该class的第一个non-inlineQnonpure virtual function的文件中吧。。(not so clearQ?/font>

在实现层面上Qtemplate g拒绝全面自动化,以手动方式在个别的object module中完成预先的L工作是一U有效率的方法?/font>

7.2异常处理

Zl持执行速度Q编译器可以在编译时期徏立v用于支持的数据结?/font>

Zl持E序大小Q编译器可以在执行期建立数据l构

c++ eH 主要׃个部分构成:

throw语句

catch语句

try语句Q这些语句可能触发catch子句起作?/font>

一个exception 被抛出时候,控制权会从函数调用释攑և来,q寻找一个吻合的catchQ如果没有那么默认的处理例程terminate()会被调用Q控制权攑ּ后,堆栈中的每一个函数调用也被推R每一个函数被推离堆栈的时候,函数的local class object 的dtor也会被调用?/font>

在程序不同段里,׃声明不同的变量,一个区域可能因为在区域内发生exception的处理方式不同分成多个区Dc?/font>

在程序员层面Qeh也改变了函数在资源上的管理。例如下面函C更含有对一块共享内存的locking和unlocking操作 Q?/font>

void mumble(void * arena){

Point *p= new point ;

smlock(arena) ;

//?.如果此时一个exception发生Q问题就产生?/font>

sumunlock(arena);

delete p;

}

从语义上Ԍ我们在函数退出堆栈之前,需要unlock׃n内存qdelete pQ我们需要这样做Q?/font>

try{smlock(arena)} catch(?{

    smunlock(arena); delete p; throw;

}

new不需要放在tryD里Q因为,如果new发生了exceptionQheap中的内存q没有分配,point的ctor没有调用Q如果在ctor中exceptionQ那么piont的Q何构造好的合成物也会自动解构掉,heap也会自动释放掉?/font>

处理q种资源理问题Q徏议: 把资源需求封装在一个class object 体内Qƈ由dtor释放资源.

auto_ptr<point> ph (new point); smlock sm(arena);//如果此时exception 没有问题

// 不需要明的unlock 和delete

// local dtor 会在q里被调?sm.SMlock::~smlock(); ph.auto_ptr<point>::~auto_ptr<point>

Exception handling 的支持:

1.验发生throw操作的函?/font>

2.军_throw是否发生在try区段?/font>

3.如果是编译器必须把exception type 拿来和catch比较

4.d的话控制权交lcatch

5.如果throw不发生在tryD|者没有吻合的Q系l会摧毁所有active local object b从堆栈把当前函数unwind?Q蟩出到E序堆栈的下一个函数去Q然后重复上q步?/font>

当一个实际对象在E序执行时被抛出Qexception object会被产生出来q常攑֜相同形式的exception 数据堆栈中?/font>

catch(expoint p){

   //do sth

   throw;

}

以及一个exception object cd?exVertex z?expoint Q这两种cd都吻合,catch会怎么?/font>

以exception object作ؓ初|像函数参C样会有一个local copyQ如果p是一个object而不是一个reference Q内容被拯的时候,q个object的非expoint部分会被切掉Q如果有vptr 会被讑֮为expoint的vptrQ如果被再次丢出呢?丢出p需要再产生另外一个exception 临时对象Q丢出原来的异常 Q之前的修改l统作废。但是如?catch(expoint& p);怎么样呢?M对这个object的改变都会繁D到之后的catch语句怅R?/font>

c++ ~译器ؓ了支持EH付出的代h大,某种E度是因为执行期的天性以及对底层g的依赖?/font>

7.3 RTTI

RTTI是Except handling的一个附属品,因ؓ我们需要提供某U查询exception objects的方法,用来得到exception的实际类型?/font>

在向下{型问题上Q如果要保证其安全性,那么必须在执行期Ҏ针有所查询Q看看它到底指向什么类型的对象。那么我们需要额外的I间存储cd信息Q通常是一个指针,指某个类型信息节炏V?/font>

需要额外的旉以决定执行期的类型?/font>

冲突发生在:

1.E序员大量的使用了多収ͼq要大量的downcast操作

2.E序员用内建的数据cd和非多态,他不需要额外负担带来的便利

那么如何了解E序员的需要呢Q? {略是一个具有多态性性质的classQ也是h内涵l承或者声?virtual func的类需要rtti支持?/font>

q样所有多态类l护一个vptr。额外负担降低到Q每一个class object多花费一个指针,指针只被讑֮一ơ,而且是编译器静态设定?/font>

down_cast

if(pfct pf = dynamic_cast< pfct >(pt))?

((type_info*)(pt->vptr[0]))->type_descripter;

Reference --------Pointer

dynamic_cast执行在ptr?p|q回0Q如果实行在ref上。由于ref不能被赋予nullQ也是没有I引用。如果我们把一个ref设ؓ0会引发时对象生,然后?初始化它Qref成ؓq个临时对象的别名。因此此时失败了会生一个bad_cast exception?/font>

typeid的参数可以引用Q对象或者是cd

事实上,type_info 也适用内徏cdQ这对于eh机制有必?/font>

例如 int ex_errno; throw ex_errno;

其中intcd int *ptr; if(typeid(ptr) == typeid(int*));

----------------------全书W记到此l束 --------------------------------s

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



rikisand 2010-03-25 21:09 发表评论
]]>
重蝲操作W?(c++primer 4e)http://www.shnenglu.com/zqsand/archive/2010/03/15/109748.htmlrikisandrikisandMon, 15 Mar 2010 08:40:00 GMThttp://www.shnenglu.com/zqsand/archive/2010/03/15/109748.htmlhttp://www.shnenglu.com/zqsand/comments/109748.htmlhttp://www.shnenglu.com/zqsand/archive/2010/03/15/109748.html#Feedback0http://www.shnenglu.com/zqsand/comments/commentRss/109748.htmlhttp://www.shnenglu.com/zqsand/services/trackbacks/109748.html1.为啥要重载操作符Q?/font>

通过重蝲操作W,E序员可以针对“类”类型的操作数定义不同的操作W版本。良好的操作W定义可以classcd的用想内置cd一L观简z,使用重定义的操作W而不是命名函C得程序可以用表达式代替函数调用,使程序编写和阅读更容易~

2.哪些不能重蝲

::     .*      .     ?:    q些不能重蝲

3.需要注意的地方Q?/font>

重蝲必须有一个classcd的操作数Q短路求值失去,

重蝲操作W和内置操作W结合型相同Q优先操作C数均相同

不要重蝲一些含有内|定义的操作W?& , && || q些

·赋|=Q下标(【】)调用 Q(Q)和成员访问操作符必须定义为成?/font>

·对称的操作符一般定义ؓ普通非成员函数

++ -- 一般设|ؓ成员函数 Q因为可能改变对象状?/font>

4.定义输入输出操作W?/font>

io操作W只能重载ؓ非成员函敎ͼ否则做操作符只能是对象成?用法变成?object<<cin  不符合我们的习惯Q经常把他们讄成ؓ友元Q因为可能触及私有变量?/font>

输入必须加入文gl束和输入错误的错误处理

istream&
operator>>(istream& in, Sales_item& s)
{
     double price;
     in >> s.isbn >> s.units_sold >> price;
     // check that the inputs succeeded
     if (in)
        s.revenue = s.units_sold * price;
     else
        s = Sales_item(); // input failed: reset object to default state
     return in;
}

如果输入p|Q调用默认构造函数恢复对象状态?/font>

5.术q算W和关系q算W?/font>

Sales_item& operator -=(const Sales_item& item){
    units_sold-=item.units_sold;
    revenue-=item.revenue;
    return *this;
}
Sales_item operator - (const Sales_item& lhs,const Sales_item& rhs){
    Sales_item res(lhs);
    res+=rhs;
    return res;
}

 

一般算术运符讄为非成员函数Q与内置q算W对应,选择q回value 而不是引用。赋D符重蝲为成员函敎ͼq用它来实现术q算W,q样术q算W不用friend

相等q算W和不等q算W一般成对出玎ͼ且用一个实现另一?/font>

关系q算W?== != > < 一般重载成非成员函?/font>

6.赋值操作符

必须为成员函?Q?P

=?=  -= 一般都需要返回左操作数的引用

Sales_item& operator = (string is){
    isbn = is;
    return *this;
}

6.下标操作W?/font>

必须为类成员函数   q回引用使其可以在赋值操作的L一?/font>

一般定义一Uconst q回帔R引用 一Unot const 引用

     class Foo {
     public:
         int &operator[] (const size_t);
         const int &operator[] (const size_t) const;
         // other interface members
     private:
         vector<int> data;
         // other member data and private utility functions
      };

int& Foo::operator[] (const size_t index)
     {
         return data[index];  // no range checking on index
     }
     const int& Foo::operator[] (const size_t index) const
     {
         return data[index];  // no range checking on index
     }

pari<string,string>& operator[] (const  vector< pair<string,string>* > ::size_type index){
    return *wait_list.at(index);//使用at判断是否界
}

6.成员讉K操作W?/font>

-> 一般要求重载ؓ成员q算W,*没有要求 ,但成员比较常见~~~

例子Qauto-ptr~~~~

ScreenPtr 的用户将会传递一个指针,该指针指向动态分配的 ScreenQ?tt>ScreenPtr cd拥有该指针,q安排在指向基础对象的最后一?ScreenPtr 消失时删除基对象。另外,不用?ScreenPtr cd义默认构造函数。因此,我们知道一?ScreenPtr 对象L指向一?Screen 对象Q不会有未绑定的 ScreenPtrQ这一点与内置指针不同。应用程序可以?ScreenPtr 对象而无首先测试它是否指向一?Screen 对象?/font>

     // private class for use by ScreenPtr only U有c,
     class ScrPtr {
         friend class ScreenPtr;
         Screen *sp;
         size_t use;
         ScrPtr(Screen *p): sp(p), use(1) { }
         ~ScrPtr() { delete sp; }
     };
        /*
      * smart pointer: Users pass to a pointer to a dynamically allocated Screen, which
      *                   is automatically destroyed when the last ScreenPtr goes away
      */
     class ScreenPtr {
     public:
         //  no default constructor: ScreenPtrs must be bound to an object
         ScreenPtr(Screen *p): ptr(new ScrPtr(p)) { }
         //  copy members and increment the use count
         ScreenPtr(const ScreenPtr &orig):
            ptr(orig.ptr) { ++ptr->use; }
         ScreenPtr& operator=(const ScreenPtr&);
         //  if use count goes to zero, delete the ScrPtr object
         ~ScreenPtr() { if (--ptr->use == 0) delete ptr; }
     private:
         ScrPtr *ptr;    // points to use-counted ScrPtr class
     };

指针支持的基本操作有解引用操作和头操作。我们的cd以这样定义这些操作:

     class ScreenPtr {
     public:
         // constructor and copy control members as before
         Screen &operator*() { return *ptr->sp; }
         Screen *operator->() { return ptr->sp; }
         const Screen &operator*() const { return *ptr->sp; }
         const Screen *operator->() const { return ptr->sp; }
     private:
         ScrPtr *ptr; // points to use-counted ScrPtr class
     };

解引用操作符是个一元操作符。在q个cMQ解引用操作W定义ؓ成员Q因此没有显式Ş参,该操作符q回?ScreenPtr 所指向?Screen 的引用?/font>

头操作W不接受昑ּ形参。point->action();   {h?nbsp; (point->action)();

可以q样使用 ScreenPtr 对象讉K Screen 对象的成员:

ScreenPtr p(&myScreen);     // copies the underlying Screen
p->display(cout);

因ؓ p 是一?ScreenPtr 对象Q?tt>p->display 的含义与?(p.operator->())->display 求值相同。对 p.operator->() 求值将调用 ScreenPtroperator->Q它q回指向 Screen 对象的指针,该指针用于获取ƈq行 ScreenPtr 所指对象的 display 成员?/font>

重蝲头操作W必返回指向类cd的指针,或者返回定义了自己的箭头操作符的类cd对象?/font>

6.自增自减操作W?/font>

一般重载ؓ成员函数Qؓ了与内置cd一_前置操作W返回运结果引用,后置操作W返回运前的|value not ref Qؓ了区分,后置操作W提供了一个实?Q?/font>

// prefix: return reference to incremented/decremented object
    CheckedPtr& CheckedPtr::operator++()
    {
        if (curr == end)
            throw out_of_range
                  ("increment past the end of CheckedPtr");
        ++curr;                // advance current state
        return *this;
    }

CheckedPtr CheckedPtr::operator++(int)
     {

         // no check needed here, the call to prefix increment will do the check
         CheckedPtr ret(*this);        // save current value
         ++*this;                      // advance one element, checking the increment 用前|实现它Q不用判断出界了
         return ret;                   // return saved state
     }

昑ּ调用Q?/font>

CheckedPtr parr(ia, ia + size);        // iapoints to an array of ints
parr.operator++(0);                    // call postfix operator++
parr.operator++();                     // call prefix operator++

7 调用操作W和函数对象

struct absInt {
       int operator() (int val) {
           return val < 0 ? -val : val;
       }
   };

 

通过为类cd的对象提供一个实参表而用调用操作符Q所用的方式看v来像一个函数调用:

     int i = -42;
     absInt absObj;  // object that defines function call operator
     unsigned int ui = absObj(i);     // calls absInt::operator(int)

函数调用操作W必d明ؓ成员函数。一个类可以定义函数调用操作W的多个版本Q由形参的数目或cd加以区别?/font>

定义了调用操作符的类Q其对象常称为函数对象,卛_们是行ؓcM函数的对象?/font>

函数Q?/font>

     // determine whether a length of a given word is 6 or more
     bool GT6(const string &s)
     {
         return s.size() >= 6;
     }

函数对象Q?/font>

// determine whether a length of a given word is longer than a stored bound
     class GT_cls {
     public:
         GT_cls(size_t val = 0): bound(val) { }
         bool operator()(const string &s)
                            { return s.size() >= bound; }
     private:
         std::string::size_type bound;
     };

for (size_t i = 0; i != 11; ++i)
     cout << count_if(words.begin(), words.end(), GT(i))
          << " words " << i
          << " characters or longer" << endl;

函数对象的便h?/font>

         plus<int> intAdd;         // function object that can add two int values
     negate<int> intNegate;   //  function object that can negate an int value
     // uses intAdd::operator(int, int) to add 10 and 20
     int sum = intAdd(10, 20);          // sum = 30
     // uses intNegate::operator(int) to generate -10 as second parameter
     // to intAdd::operator(int, int)
     sum = intAdd(10, intNegate(10));    // sum = 0

函数适配器:

banding器,它通过一个操作数l定到给定D将二元函数对象转换Z元函数对?/font>

求反器是一U函数适配器,它将谓词函数对象的真值求反。标准库定义了两个求反器Q?tt>not1 ?not2 分别求反一元二元对?/tt>

8。实参匹配和转换Q俺来看重蝲操作W的原因啊,Q,Q?/font>

转换操作W是一U特D的cL员函数。它定义类cdD{变ؓ其他cd值的转换。{换操作符在类定义体内声明Q在保留?operator 之后跟着转换的目标类型:

转换函数采用如下通用形式Q?/font>

     operator type();

转换函数必须是成员函敎ͼ不能指定q回cdQƈ且Ş参表必须为空?/font>

虽然转换函数不能指定q回cdQ但是每个{换函数必L式返回一个指定类型的倹{例如,operator int q回一?int |如果定义 operator Sales_itemQ它返回一?Sales_item 对象Q诸如此cR?/font>

转换函数一般不应该改变被{换的对象。因此,转换操作W通常应定义ؓ const 成员?/font>

只要存在转换Q编译器在可以使用内置转换的地方自动调用它

  • In expressions:

    在表辑ּ中:

         SmallInt si;
         double dval;
         si >= dval          // si converted to int and then convert to double
  • In conditions:

    在条件中Q?/font>

         if (si)                // si converted to int and then convert to bool
  • When passing arguments to or returning values from a function:

    实参传l函数或从函数返回|

         int calc(int);
         SmallInt si;
         int i = calc(si);      // convert si to int and call calc
  • As operands to overloaded operators:

    作ؓ重蝲操作W的操作敎ͼ

         // convert si to int then call opeator<< on the int value
         cout << si << endl;
  • In an explicit cast:

    在显式类型{换中Q?/font>

         int ival;
         SmallInt si = 3.541; //
         instruct compiler to cast si to int
         ival = static_cast<int>(si) + 3;
    

    cȝ型{换之后不能再跟另一个类cd转换。如果需要多个类cd转换Q则代码出错。(指的是不能连l两个自定义的类型{换,但是内置cd转换可以的)

  • q有一部分是实参匹配和转换 Q没旉?以后再看~~~~

     

     

     

     

     

     

     

     

     

     

     



    rikisand 2010-03-15 16:40 发表评论
    ]]>
    素数{法http://www.shnenglu.com/zqsand/archive/2010/03/12/109528.htmlrikisandrikisandFri, 12 Mar 2010 06:04:00 GMThttp://www.shnenglu.com/zqsand/archive/2010/03/12/109528.htmlhttp://www.shnenglu.com/zqsand/comments/109528.htmlhttp://www.shnenglu.com/zqsand/archive/2010/03/12/109528.html#Feedback0http://www.shnenglu.com/zqsand/comments/commentRss/109528.htmlhttp://www.shnenglu.com/zqsand/services/trackbacks/109528.html

    #include <iostream> 
    #include <vector>
    #include "time.h"
    using namespace std; 
    void sieve(int n){
        vector<bool> isprime(n,true);
        vector<int> prime;
        int cnt=0;
        for(int i=2;i<n;i++){
            if(isprime[i])cnt++,prime.push_back(i);
            for(int t=0;t<cnt&&i*prime[t]<n;t++){
                isprime[i*prime[t]]=false;
                if(i%prime[t]==0)break;
            }
        }
        /*for(int i=0;i<cnt;i++)
            cout<<prime[i]<<" ";*/
    }
    void oldseive(int n){
        vector<bool> isprime(n,true);
        vector<int> prime;
        for(int i=2;i<n;i++){
            if(isprime[i]){
                prime.push_back(i);
                for(int j=i*2;j<n;j+=i)
                    isprime[j]=false;
            }
        }
        /*for(int i=0;i<prime.size();i++)
            cout<<prime[i]<<" ";*/
    }
    int main(){
        clock_t start,end;
        start = clock();
         sieve(2000000);
         //oldseive(2000000);
        end  = clock();
        double time = double(end-start)/CLOCKS_PER_SEC;
        cout<<endl<< time<<endl;
    } 

    U性筛法sieve 1.546s oldsieve 2.875s 快了近一?/p>

    old sieve ~陷Q合数可能被多次{掉Q例?30?Q?Q?{掉??然后 U性筛法限定了 M一个合数只被它的最质因数{掉一ơ,怎么做到q一点~~

    if(i%prime[t]==0) break; 如果此时{掉的合C到大找到第一个可以整除的质数Q那么显然他扑ֈ了它的最质因数Q此时我们停止搜索质数表Q因为后面的质数比当前的prime[t]要大Q如果我们用prime[t+n]*i {掉了一个合敎ͼq个合数必然可以表述成ؓ prime[t]*someK  *prime[t+n] 也就是说q个合数的最质因数也是prime[t],他应该被 prime[t]{掉-->当程序运行到 someK*prime[t+n] 的时候~~~~

    over--------------------------------------------------------------------



    rikisand 2010-03-12 14:04 发表评论
    ]]>
    深入探烦C++对象模型MW记 (?http://www.shnenglu.com/zqsand/archive/2010/03/09/109322.htmlrikisandrikisandTue, 09 Mar 2010 14:20:00 GMThttp://www.shnenglu.com/zqsand/archive/2010/03/09/109322.htmlhttp://www.shnenglu.com/zqsand/comments/109322.htmlhttp://www.shnenglu.com/zqsand/archive/2010/03/09/109322.html#Feedback1http://www.shnenglu.com/zqsand/comments/commentRss/109322.htmlhttp://www.shnenglu.com/zqsand/services/trackbacks/109322.htmlFunction 语义?/font>

    ····member 的各U调用方式~

    1.非静态成员函敊W?/font>

    float Point3d::mangitude3d()const{…}

    会变?float Point3d::magnitude(const Point3d* this){…}

    c++的准则之一Q非静态成员函数至必d一般的nonmember function 有相同的效率

    名称的特D处理:Qname manglingQ一般member名称前面都会加上class名称QŞ成独一无二的命名,class Bar {public Qint ivalQ} 可能变成 ival_3Bar ;3应该是bar的长度了?/font>

    q样可以防止l承体系中两个类定义同样名字的变量~

    如果使用extern “C?可以压制nonmember 的mangling 效果

    2.虚拟成员函数·

    如果normalize 是虚拟函?他会被翻译成:

    (*ptr->vptr[1])(ptr); W二个ptr是this指针

    cM的magnitude 会变?/font>

    (*this->vptr[2])(this);

    而magnitude是在normalize之后调用的因此此时已l确定this指向的是Point3d 因此可以直接调用Point3d::magnitude()更有效率

    如果用一个对象调用一个虚拟函数应该把它当做正常函数来对待Q因为可以确定对象类型直接调用相应的函数卛_Q在q种情况下,虚拟函数也可以inline 来提高效率了~~~

    3.静态成员函?/font>

    class A{
    public:
        static int a;
        static int geta(){return a;}
    };
    int A::a=33333;
    int main(){
        cout<< ((A*)0)->geta()<<endl;
    }

    static的主要特征是他没有this 指针Q这样导致?/font>

    他不能直接存取其class中的nonstatic members

    他不能被声明为const volatile 或者virtual

    他不需要经由class object 才被调用 虽然大部分情冉|q样调用?/font>

    如果取一个static member func 地址则得到的是他在内存中的真正地址Q而且得到的是一个函数指针,而不是一个指向class member 函数的指?/font>

     

    ····虚拟成员函数

    Z支持virtual func 机制Q必首先能够对多态对象由某种形式的运行期cd判断Ҏ

    c++中多态表C:以一个public blase class 指针或者引?dZ个derived class object 的意?/font>

    识别出哪个类需要支持多态只要看他是否有M的virtual func

    ~~~单一l承

    vtable中每一个virtual funcQ包括pure funcQ都被指z一个固定的索引|q个索引在整个承体pM保持与特定的virtual function 的关?/font>

     

    当一个class l承自上一个class时?/font>

    1.可以l承base class 声明的virtual func Q这栯函数实体的地址会被拯C的vtable相对应的slot 中,位置x不变 q样调用时?ptr->func();会翻译成 (*ptr->vtbl[x])func(ptr) ;而不用管ptr 到底是一个base q是一个derived

    2.他可以用自q函数实体Q表CZ自己的函数地址必须攑֜相应的位|x?,跟上面的例子一?/font>

    3.可以加入新的virtual 函数Q这时候vtbl 会变?/font>

    ~~~多重l承?/font>

    多重l承时?例如 Derived public ?Base1,Base2

    Base2 *pbase2 = new Derived; 新的Derived必须调整

    Base2 *pbase2 = tmp?tmp+sizeof(Base1):0;

    当程序员删除pbase2指向的对象时指针必须再一ơ调整。上q的调整q不能在~译时期讑֮Q因为pbase2指向的对象只有在执行期才能确定?/font>

    同样道理Qpbase2 如果要调用函数的话,调用操作会引发必要的指针调整Q也必须在执行期调整?/font>

    Bjarne采用扩充vtable 每一记录调整this指针的信息,但浪费,因ؓ大部分不需要调?/font>

    Thunk技术是用一D|~实现调整this指针以及跛_virtual func的过E?/font>

    调整this指针的第二个负担是:如果由derved class 调用Q或者由W二个base class 调用Q同一个函数可能在virtual table 对应多个slots

    pbase1 和derived 的vtable可以合ƈQ他们用同样的slots 偏移Q里面可以放真正的地址Q而pbase2 需要调整this指针Q其vtabl 相应的地址攄是相应的thunk地址?/font>

    可以看到”:

    1.如果通过指向W二个base class 指针调用derived的func ptr 需要调?/font>

    2.如果通过指向derived指针调用从第二个l承来的func 需调整

    3.如果允许virtual func q回cd有所变化Q可能base 可能derivedQ也需要调整this

    Microsoft 用address point {略Q即用来改写别人的函数Q期待获得的参数QthisQ是引入该class 的地址Q这是函数的address classQ~~不了啊~~Q?/font>

    ~~~虚拟l承下的virtual func

      即便只有一个base clas 它的布局转换也需要this 指针的调_相当复杂~~~

    …指向成员函数的指针

    double Point::x();

    可以定义指向成员函数的指?/font>

    double (Point::* pmf)()=&Point::x;

    调用可以  (origin.*pmf)() 或?ptr->*pmf();

    如果是虚拟函数的指针呢?Q?/font>

    Point* ptr= new Point3d;

    如果x是一个虚拟函?/font>

    (ptr->*pmf)();仍然是Point3d::x()被调用么Q?/font>

    {案~~是的

    因ؓ取得虚拟函数的地址其实取得的是虚拟函数的offset?/font>

    调用会变?nbsp; (*ptr->vtbl[(int)pmf])(ptr);

    class A{
    public:
        static int a;
        static int geta()  {return a;}  //静态ƈ不能作ؓ重蝲条g
        int geta(int x){
            return a;
        }
         int  geta( int  a)const{} // const成员函数 Q可以作为重载条?br>};
    int A::a=33333;
    int main(){
        A a;
        cout<< ((A*)0)->geta()<<endl;//静态成员函数的一U调用方?((A*)0)->geta()
        int(*p)()= &A::geta;
        cout<<(*p)()<<endl;
        int (A::* pgeta)(int a) = &A::geta;
        cout<<(a.*pgeta)(3)<<endl;
    }

    输出均ؓ33333 

    多重l承下呢Q?Q?

    Microsoft提供?U解x法:

    一U:单一l承的情况(带vcall thunk地址或者函数地址Q?/font>

    2多重l承 带有faddr 和delta

    虚拟l承 带有四个members

    Q·····具体以后再查吧Q?/font>

    ----------

    inline members

    真正的inline 函数扩展是在调用的那一个点上,q回带来参数的求值操作以及暂时性对象的理

     

    形式参数 formal arguments

    在inline 期间 每一个Ş式参数都会被相应的实际参数取代,副作用是Q不可以只是单的一一塞E序中出现的每一个Ş式参敎ͼ因ؓq将D对于实际参数的多ơ求值操作,可能产生 带来副作用的 实际参数Q通常q需要嵌入实际对象的~~~~

    所以,如果实际参数是常量,那么我们可以直接l定Q如果不是常量也没有副作用,我们直接代替Q否则~~~暂时对象会需要的~·

    例如Q?/font>

    inline int min(int i,int j) { return i<j ? i:j ;}

    minval = min(val1,val2);

    minval = min(11,12);

    minval = min (foo(),bar()+1);

     

    q会扩展? minval = val1<val2 ? val1?val2;

    minval = 11;( 帔R?

    int t1,t2; minval =(t1 = foo()), (t2=bar()+1),t1<t2?t1:t2;

    如果我们改变函数定义

    {int minval = i<j?i:j; return minval;}

    如下调用{int minval ; minval = min(val1,val2);}

    Zl护局部变量可能会变成Q?/font>

    { int m_lv_minval; minval=(__min_lv_minval=val1<val2?val1:val2),min_lv_minval;}

    一般而言Qinline 函数的每一个局部变量都必须攑֜函数调用的一个封闭区D中Q拥有一个独一无二的名字,如果inline函数以单一表达式扩展多ơ,那么每次扩展都需要自q一l局部变量。如果inline 函数可以以分ȝ多个式子被扩展多ơ,那么只需要一l局部变量就可以重复使用Q因Z们被闭在自qscope中:

    例如 minval = min(val1,val2) + min(foo(),foo()+1) ;

    扩展 int __min_lv_minval_0,__min_lv_minval_1,t1,t2;

    minval = ((__min_lv_minval_0 = val1<val2?val1:val2),__min_lv_minval_0)+?;

    参数带有副作用或者是以一个单一表达式做多重调用Q或者是在inline 函数内部有多个局部变?/font>

    都会产生局部变量,要小心对?/font>

    --------------------l束U哦~~~~~~··----------------------

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     



    rikisand 2010-03-09 22:20 发表评论
    ]]>
    深入探烦C++对象模型MW记 (?http://www.shnenglu.com/zqsand/archive/2010/03/07/109118.htmlrikisandrikisandSun, 07 Mar 2010 08:39:00 GMThttp://www.shnenglu.com/zqsand/archive/2010/03/07/109118.htmlhttp://www.shnenglu.com/zqsand/comments/109118.htmlhttp://www.shnenglu.com/zqsand/archive/2010/03/07/109118.html#Feedback0http://www.shnenglu.com/zqsand/comments/commentRss/109118.htmlhttp://www.shnenglu.com/zqsand/services/trackbacks/109118.htmlDATA 语义?/font>

    q段代码输出什么?

    #include <iostream>
    using namespace std;
    class A{ public:A(){ac='s';}private:char ac;};
    class B:virtual public A{public:B(){a='e';}char a; };
    class C:virtual public A{ };
    class D:public  B,public  C
    {
    public:
        D():A(),B(),C(){b=13;}
        int b;
    };
    int main(){
        D d;
        cout<<"sizeof(A)="<<sizeof(A)<<endl;
        cout<<"sizeof(B)="<<sizeof(B)<<endl;
        cout<<"sizeof(C)="<<sizeof(C)<<endl;
        cout<<"sizeof(D)="<<sizeof(D)<<endl;
        cout<<endl;
        cout<<"address of A's subobject in d"<<(A*)&d<<endl;
        cout<<"address of B's subobject in d"<<(B*)&d<<endl;
        cout<<"address of C's subobject in d"<<(C*)&d<<endl;
        cout<<"address of D's subobject in d"<<(D*)&d<<endl;
        cout<<endl;
        int* p = (int*)(*((int*)&d));
        cout<<"address of b's virtual base table="<<p<<endl;
        cout<<"first member in b's virtual base table="<<*p<<endl;
        cout<<"second member in b's virtual base table="<<*(p+1)<<endl;
        cout<<"third member in b's virtual base table="<<*(p+2)<<endl;
        cout<<endl; 
        p= (int*)*((int*)((C*)&d));
        cout<<"address of c's virtual base class table= "<<p<<endl;
        cout<<"first member in c's virtual base table="<< *p<<endl;
        cout<<"second member in c's virtual base table="<<*(p+1)<<endl;
        cout<<"third member in c's virtual base table="<<*(p+2)<<endl;
        char *pchar= (char*)(&d)+4; //char型加4   注意A中的ac其实是私有变量,B不应该可以访问到的,实际上通过强制转换可以非法触及??
        cout<<*pchar<<endl;
        cout<<*(pchar+12)<<endl;
        B b;
        int *pint =(int*)(&b)+1; //int?1 
        cout<<*((char*)(pint))<<endl;
        pint = (int*)(&d)+3;
        cout<<*(pint)<<endl;
    }

     

    l果是:

    sizeof(A)=1
    sizeof(B)=9
    sizeof(C)=5
    sizeof(D)=17

    address of A's subobject in d0012FF74
    address of B's subobject in d0012FF64
    address of C's subobject in d0012FF6C
    address of D's subobject in d0012FF64

    address of b's virtual base table=00403350
    first member in b's virtual base table=0
    second member in b's virtual base table=16
    third member in b's virtual base table=0

    address of c's virtual base class table= 00403358
    first member in c's virtual base table=0
    second member in c's virtual base table=8
    third member in c's virtual base table=0
    e
    s
    e
    13

     

    1.语言本n造成的负担:如virtual baseclass

    2.寚w造成的负担(寚w会单独开题讨论)

    3.~译器优化处?A虽然是空?but Z让A的两个object在内存中得到不同的地址Q编译器l他加上了一个byteQ但是B和C都没有这一个byte呢?q是~译器做了优化,允许省掉q一个byte

    看上面的代码”:

    环境是vs2008 寚w讄?字节

    A的大ؓ1 因ؓ有一个char 没有寚w因ؓ不是l构型对象,如果A没有q个char大小依然??/font>

    B的大ؓ9 首先开始是它的virtual base class ptrQ?个字节的指针Q然后是他自qmember 1个char 此时Z保证其对象完整性编译器寚w?字节处也是在第九个字节内放入基cȝmember

    q样如果我们把A=B B赋给AQ传输可以从?字节开始割出A卛_

    C的大是5 没什么好解释

    D的大是17首先是B?字节(4字节vbptr+1字节char member+3字节寚w)然后是C?字节vbptr,然后是自qmember4字节最后是1字节的base class member,可以看到B和C的base class table中的w是自׃|与q个member的offset ?/font>

     

    不同~译器可能结果不同的Q因为c++ standard q没有强制规?base class subobjects的顺序等

     

    data member 是程序执行过E中的某U状态:

    static data member 是整个class 的状?/font>

    non-static data member 是个别class-object 的状?/font>

    c++对象量以空间优化和存取速度的考虑来表现non-static members Qƈ且和c的struct 数据配置的兼Ҏ?/font>

    static data member 攑֜E序的一个global data segment 中,不会影响个别的class-object 大小

    Q在class没有object 时已l存在,但是template中有些不?/font>

     

    -----DATA member 的绑?/font>

    始终把nested type 声明 攑֜class 起始处,argument list 中的名称会在W一ơ遇到时候被适当的决议完成,因此extern 和nested type names 之间的非直觉l定操作q是会发生?/font>

    ---- DATA 的存?/font>

    Point3d origin,*pt=&origin;

    origin.x = 0.0; ?pt->x=0.0 ; 有什么区别么Q?

    如果x是静态data member 则完全没有区?因ؓ他们都在data segment 中和object无关

    ~nonstatic data member---------

    如果point3d不包含虚拟扉K么没有差?/font>

    否则我们不能定pt中必然指向哪一U因此不能在~译器确定offset需要一些运行时候的计算抉择Q而origin则不同一定是某一个类型的所以没有问?/font>

    多承或者单l承都不会带来访问上的媄响,因ؓ他们都可以向c的结构体那样在编译时期确定各个member的offset。即使是多承pt指向W二个baseclass的dataQ由于member的位|在~译时期已l固定了Q因此存取member只是一个offsetq算Q像单一l承那样单,不管是指针,reference或者object都一?

    只有virtual base class 会带来一些损耗,因ؓ他得对象模型变得复杂了

    如果我们在一个类中加入了virtual func 会发生什么~~~

    1. 会生一个virtual tableQ存放每一个virtual func地址以及rtti支持的type_info

    2.class object 内都加入一个vptrQ提供执行期的链?/font>

    3.加强ctor 讑֮vpr初?/font>

    4.加强dtor 消除vptr 从dirived class ?base class

     

    虚拟l承Q?/font>

    他必L持某UŞ式的“shared subobject”?/font>

    那么一个object会分成一个不变局部和一个共享局部的数据Q共享局部就是virtual base class subobjectQ他的位|会因ؓ每次z操作发生变化(例如一个virtual base class subobject的位|在不同U别的承体pM的位|是不确定的Q不像多l承单扉Kh固定的offset)Q所以他只能间接存取Q因此生了效率~损.(间接是指的是他只能首先读Z的指针,然后Ҏ指针的内容取C)

    所以在虚拟l承基类中最好不要有data member

    -------指向DATAmember 的指?/font>

    #include <iostream>
    using namespace std;

    class  A
    {
    public:

        int a;
    };
    int main(){
        int  A::* p=&A::a;
        cout<<p<<endl;
    }

    输出 1

    因ؓZ防止&A::a?int A::*a = 0Q一h他加??

     

    虚拟l承带来的主要冲LQ妨了优化的有效性,因ؓ每一层虚拟扉K带来了一个额外层ơ的间接性,在编译器中存?cMpoint::x的操作pb.*bx

    会被转化?&pb->vbcPoint+bx

    而不是{换成 &pB +bx

    额外的间接性会降低“把所有操作都搬移到缓存器中执行”优化能力,也就是降低了局部性~

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     



    rikisand 2010-03-07 16:39 发表评论
    ]]>
    深入探烦C++对象模型MW记 (?http://www.shnenglu.com/zqsand/archive/2010/03/03/108832.htmlrikisandrikisandWed, 03 Mar 2010 14:23:00 GMThttp://www.shnenglu.com/zqsand/archive/2010/03/03/108832.htmlhttp://www.shnenglu.com/zqsand/comments/108832.htmlhttp://www.shnenglu.com/zqsand/archive/2010/03/03/108832.html#Feedback4http://www.shnenglu.com/zqsand/comments/commentRss/108832.htmlhttp://www.shnenglu.com/zqsand/services/trackbacks/108832.html构造函数语义学

    --了解~译器在构造对象时背着我们q了什么勾?/font>

    Default Ctor 在需要的时候被构徏出来~

    什么需? 是编译器需要而不是程序的需?所以不要期望编译器生成的Ctor会帮我们把我们的成员变量初始化ؓ零。那是程序的需要,也就是我们程序员的Q务···?/font>

    例如Q?/font>

          class Foo(public:int val;);

          void foo_bar(){Foo bar; if(bar.val)dosth;} 不要期望~译器帮我们初始化它?。只有global变量会初始化?Q初始化valq样的变量需要我们自己写代码的~~

    Default Ctor分ؓtrivalQ没用的Q和non-trival的,下面讨论什么时候编译器需要ctor 也就是有用的ctorQ这时候如果我们没有提供一个默认的default ctor 它会帮我们合成一个的~~

    1.带有Default Ctor 的member class object

    很好理解Q既然内部成员含有default ctor 那么我们创徏新的对象旉要调用它Q而我们ƈ木有调用它的函数Q编译器自然会帮我们提供一个。如果我们提供了default ctor Q那么编译器会在我们提供的函数加入调用必调用的member class 的default ctor。同L在每一个ctor里面都将在用户写的代码之前调用需要调用的default ctor -------看例子:

    class Dopey();class Sneezy{public:Sneezy(int val);Sneezy();};class Bashful{public:BashFul();};

    class Snow_white{public: Dopey dopey;Sneezy sneezy;Bashful bashful;private:int muble};

    如果Snow_white没有定义default ctor 那么~译器会创徏一个,q在其中依照声明序依次调用dopey sneezy bashful的default ctor 然而如果:

    Snow_white::Snow_white():sneezy(1024){muble=2045;}

    ~译器会扩张?/font>

    Snow_white::Snow_white():sneezy(1024){

      dopey.Dopey::Dopey();

      sneezy.Sneezy::Sneezy(1024);

      bashful.Bashful::Bashful();

      /////////   explicit user code

      muble = 2048;

    }

    2.z自带有Default ctor ?base class

    同样道理如果父类有Default ctor 子类当然要调用,~译器会为想上面一样ؓ我们加上

    3.含有virtual functions的Class

    创徏object 时候需要ctor 来设|好vptr

    4.带有virtual base class

    virtual base 实现Ҏ各有不同Q然而共同点是必Lvirtual base class 在其每一个derived class中的位置能够与执行期准备妥当

    X A B C 菱Şl承

    void foo(const A*pa){pa->i=1024;}

    foo(new A);foo(new C);

    知道pa ?i在对象中的位|ƈ不是固定的,而是在运行时真正定pa指向什么对象Aq是C才能定的,因此需要设定一个指向基csubobject的指针,所以需要ctor工作?/font>

    OK:

    1.Mclass 如果没有定义Default ctor ׃被合成一?/font>

    2.合成出来的Default ctor 会明设定每一个data member?/font>

    错的很easy了~

    -------------------------------------------------------------------------------

    再来?Copy CtorQ?/font>

    copy ctor 负责用另一个对象初始化一个对?/font>

    operator = 负责用另一个对象给一个对象赋?/font>

    直接赋值时Q传参时Q返回时可能调用Copy ctor

    Default member initialization~~~

    也就是memberwise 的initialization

    他会把每一个data member Q内建的或者派生的Q从一个object 拯到另一个object中去

    如果object允许bitwise的拷贝那么编译器׃用生成一个nontrival的default copy ctor

    什么时候不可以呢~

    1 内含一个member object 而后者含有copy constructor (声明或者合成的)

    2  l承一个base class 后者有copy ctor

    3  含有virtual func

    4  z自一个扉KQ其中有virtual base class

    1? 中编译器会把member 或者baseclass copy ctor 调用安插在合成的copy ctor ?/font>

    3 中:

    如果两个同样cd的object赋值时Q没有问题因Z们的vptr相同

    但是考虑子类赋值给父类Q此时vptr需要更改,那么此时不具有bitwiseҎ,因此需要编译器来加入语句正更新vptr

    4中:

    每个~译器都承诺必须让derived class 中的virtual base class object 在执行期间准备妥当,l护位置完整性是~译器的责Q。bitwise copy 有可能会破坏q个位置所以编译器需要在自己合成的copy ctor 中作Z?/font>

    同样问题发生在承体pM子类向父c赋值时Q由于对象模型问题,直接bitwise复制可能会导致base class object 的破坏(后面章节会有讨论Q?/font>

    --------------------------------------------------------------------------------

    E序转化语义?

    X x0;

    void foo(){X x1(x0); X x2=x0; X x3=X(x0);}

    转化Q重写定义,初始化操作会被剥?nbsp;  copy constructor 调用会被安插

    void foo(){ X x1;X x2; X x3;  x1.X::X(x0); x2.X::X(x0); x3.X::X(x0);}

     

    参数的初始化Q?/font>

    一U策略导入暂时的object

    void foo(X x0);X xx; foo(xx);

    转化Q?/font>

    X _tmp;

    _tmp.X::X(x0); foo(_tmp);

    foo变成 void foo(X& x0);

    另一U是拯构徏Q?/font>

    把实际参数直接徏构造应该在的位|上Q函数返回时局部对象的destructor 会执?/font>

    也就是说把x0建构在foo 的函数执行的地方

     

    q回值的初始化:

    X bar(){

        X xx;

        return xx;

    }

    q回值如何从局部对象xx拯而来呢?

    一U方法:1.加上额外参数Q类型是class object的referenceQ这个参数用来放|被拯建构的返回?Q注意拷贝徏构也是说他被放在应该在的位|,也就是说不是局部变量了Q?/font>

    2.return 指o之前安插一个copy constructor 调用操作 Q以便将传回的object 内容当做上述参数的初?/font>

    so 上面的程序变成了Q?/font>

    void bar(X& _result){

      X xx;

      xx.X::X(); //~译器生的default ctor 调用

      _result.X::X(xx);//~译器生的copy ctor 调用

      return ;

    }

    现在~译器必{换bar的调用操?X xx=bar();转换?X xx; bar(xx); // 注意不用copy ctor了直接操作在xx上了 如果~译器做了优?q就是named return value 优化

    而:

    bar.memfunc();//bar()传回的X 调用成员函数

    变成Q?/font>

    X _tmp;  (bar(_tmp),_tmp).memfunc();

    同样道理函数指针 X(*pf)(); 变成 void (*pf)(X&);

    使用者的优化:

    X bar(const T& y,const T& z){

        X xx;

        通过 y z 计算xx

       return xx;

    }

    q种情况下要iuxx被memberwise拯到编译器产生的_result中,

    如果定义 ctor来利用yz计算xx则:

    X bar(const T& y,const T& z){

         return X(y,z);

    }

    变成Q?/font>

    bar(X& result){

         result . X::X(y,z);

         return;

    }

    无需copy ctor?/font>

     

    ~译器的优化Q?/font>

    如上所qbar中返回了具名数?named valueQ因此编译器有可能自׃化,Ҏ是以result取代named return value

    bar(X& result){_result.X::X(); 直接处理result q不处理变量xx然后复制lresult q样׃化了}

    q个优化的激z需要class提供一个copy ctor~~~~~~

     

    Copy ctor 要不?

    如果一个class W合bitwise的要求那么此时member wise 的拷贝已l高效简z?无需加入?/font>

    但是如果class需要大量的member wise 初始化操作,如用传值方式返回objectsQ如果是q样提供一个copy ctor 可以ȀznRV named return value 优化Q这很合理?/font>

     

    成员们的初始化过E:

    什么时候必ȝ初始化列表:

    1.初始化一个reference?2.初始化一个const member 3.调用父类ctor而且有参数时4调用member class ctor 有参?

    其他情况呢:

    class word{string name;int cnt;public: name=0;cnt=0;}

    ~译器可能这么做:

    word::word{

        name.String::string();调用string的default ctor

        string tmp=string(0);

    _name.string::operator=(tmp);

    tmp.string::~string();

    cnt=0;

    }

    昄name攑ֈ初始化列表会更有效率 Q会变成

    name.String::String(0);

    而cntq种内徏cd则没有关p,放不攑ֈ初始化列表没有效率上的差?/font>

    初始化列表究竟让~译器做了什么?Q?Q?/font>

    ~译器会一个个操作list中的式子Q以适当ơ序在ctor内安插初始化操作在Q何explicit user code 之前?/font>

    注意的地方:

           list中的ơ序是按照members声明ơ序军_而不是list 中的排列序军_?/font>

    例如Qclass x{int i;int j; X(int val):j(val),i(j)}

    错了 i先声明则i首先赋予val 然后用未初始化的j赋给i。。?/font>

    可以q样X::X(int val):j(val){i=j;}

    ׃会安插在explicit code 之前 所以没问题 会变?j=val;   i=j;

    可否用member functions 初始化成员?Q?/font>

    {案是可以的Q因为和objects相关的this指针已经构徏妥当Q只是要注意函数调用的member是否已经构徏妥当了即?/font>

    ------         -  -  - ----------       --       --疲惫的结束线-          - -  -     - --            -           -----

    name return value TEST:

    ~~~~1 cl /od 不开优化

    #include <iostream>
    using namespace std;
    class RVO
    {
    public:

        RVO(){printf("I am in constructor\n");}
        RVO (const RVO& c_RVO) {printf ("I am in copy constructor\n");}
        ~RVO(){printf ("I am in destructor\n");}
        int mem_var;      
    };
    RVO MyMethod (int i)
    {
        RVO rvo1;
        rvo1.mem_var = i;
        return (rvo1);
    }
    int main()
    {
        RVO rvo;rvo=MyMethod(5);
    }

    输出Q?

    I am in constructor         //rvo 创徏
    I am in constructor         // rvo1创徏
    I am in copy constructor // rvo1赋值给hiddern
    I am in destructor          // rvo1解构
    I am in destructor          // hiddern解构
    I am in destructor         //  rvo 解构

    A MyMethod (A &_hiddenArg, B &var)
    {
       A retVal;
       retVal.A::A(); // constructor for retVal
       retVal.member = var.value + bar(var);
       _hiddenArg.A::A(retVal);  // the copy constructor for A
       return;
    retVal.A::~A();  // destructor for retVal
    
    }
    A MyMethod(A &_hiddenArg, B &var)
    {
       _hiddenArg.A::A();
       _hiddenArg.member = var.value + bar(var);
       Return
    }
    ~~~~2 cl /o2 代码同上
    output

    I am in constructor  //rvo创徏
    I am in constructor  //hiddern 创徏
    I am in destructor   //hiddern 解构
    I am in destructor   //rvo解构

    我不明白的是hiddern 怎么传给rvo Q我猜可能是~译器按照bitwise的复制方式进行的,此时~译器ƈ没有直接建构l果于rvo?/strike> Q看看下面的试验Q?/font>

    注:明白了, l果直接建构在hiddernQ然后通过operator = 传给rvo 。没有copy ctor因ؓ拯构造函数是负责初始化的Q而operator = 才是用来赋值的.

    l过代码证明是对的,如果重蝲赋D符 输出变成Q?/font>

    I am in constructor  //rvo创徏
    I am in constructor  //hiddern 创徏

    I am in operator =   //赋值操作~~
    I am in destructor   //hiddern 解构
    I am in destructor   //rvo解构

    ~~~~3 cl /od

    #include <iostream>
    using namespace std;
    class RVO
    {
    public:

        RVO(){printf("I am in constructor\n");}
        RVO (const RVO& c_RVO) {printf ("I am in copy constructor\n");}
        ~RVO(){printf ("I am in destructor\n");}
        int mem_var;      
    };
    RVO MyMethod (int i)
    {
        RVO rvo1;
        rvo1.mem_var = i;
        return (rvo1);
    }
    void abc(RVO& i){
    }
    int main()
    {
        RVO rvo=MyMethod(5);  //此时定义和赋值放C一个表辑ּ?br>    return 0;
    }

    output:

    I am in constructor           // rvo1 创徏 注意 跟上面的W一U情况下的hiddern一?rvoq没有调用ctor
    I am in copy constructor   // rvo1 拯构造给rvo 此时没有hiddern?直接构徏rvo?br>I am in destructor            // rvo1 析构
    I am in destructor            // rvo1 解构

    ~~~~3 cl /o2  再来~~~~ NRV出马

    I am in constructor           // rvo构徏?nbsp;
    I am in destructor            // rvo析构?/font>

    此时 mymethod中的一切都直接反映在rvow上

    ok~~~~~4个代码完全一h造析构拷贝函C数由2-6不等~~~over~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     



    rikisand 2010-03-03 22:23 发表评论
    ]]>
    深入探烦C++对象模型MW记 (一)http://www.shnenglu.com/zqsand/archive/2010/03/03/108780.htmlrikisandrikisandWed, 03 Mar 2010 05:58:00 GMThttp://www.shnenglu.com/zqsand/archive/2010/03/03/108780.htmlhttp://www.shnenglu.com/zqsand/comments/108780.htmlhttp://www.shnenglu.com/zqsand/archive/2010/03/03/108780.html#Feedback0http://www.shnenglu.com/zqsand/comments/commentRss/108780.htmlhttp://www.shnenglu.com/zqsand/services/trackbacks/108780.html寒假基本M一遍,现在再读一遍,做下W记?/font>

    W记当做的精D有意义Q而后回顾可知其意Q回其味Q方有成?/font>

    ---------------------------------------------------------------------------------

    C语言Q数据和操纵数据的方法分开声明,Ҏ被写成函数处理外部数?/font>

    C++:数据和方法的l合装ADT 更安?支持l承 清晰

    装在空间上的代P

    如果仅仅是普通承则q没有什么代P因ؓ每个数据成员直接内涵在每一个class对象中,和c中的struct一P而member function如果不是inline的则只会产生一个实体,而inline会在每一个用的模块产生一个函数实?/font>

    c++在空间和存取效率的代价主要由virtual带来Q?/font>

    virtual function 机制Q?vtable vptr 执行期绑?/font>

    virtual class 机制Q?/font>

    q有多重l承下的额外负担Q子c{换成W二个父cȝ时候)

    ----------------------------------------------------------------------

    c++对象模型Q也是我们怎么在内存中安排各个数据成员以及成员函数

    1.单对象模型~ 对象由各个slotl成Q每一个slot包含指针Q指向各个成?/font>

    2.表格驱动模型~ 对象含有两个指针Q一个指向成员数据表Q另一个指向成员函数表

    3.c++实际对象模型~ 非静态数据被攑֜对象内,而静态数据成员放在对象外Q显然啊Q他属于c)Q成员函?/font>

      不管静态或者非静态都攑֜对象外面

      Virtual functions Q每个class产生一堆指向vitrualfunctions的指针,攑֜vtables中,每个对象

      含有vptr 指向vtable vptr的设定,重置均有class的ctorQdtorQcopy assignment q算W自动完?/font>

      优点Q空间和存储效率 ~点Q如果对象模型的nonstatic data members 有所修改Q也是对象内部布局有所更改Q那么用该对象的应用程序需要重新编?

    虚拟l承Q不基c被z了多次Q永q只有一个实体(菱Şl承最下面对象中只有一个顶部类实体Q?/font>

    l承的实玎ͼ一U可以用basetable 在basetable中指向一个baseclass的地址Q但q会随着l承深度来深变得?/font>

    现在采用的方法之后记录~~~ QP

    书中的程序:对象模型对程序的影响Q?/font>

    X foobar(){
        X xx;
        X *px = new X;
        xx.foo();
        px->foo(); //foo是一个virtual function
        delete px;
        return xx;
    }
    //调用?X _result;
    //      foobar(_result);
    void foobar(X& _result){
        _result.X::X();//构造result
        px = _new(sizeof(X));
        if(px!=0)
            px->X::X;
        foo(&_result);//使用result取代xx但是不激zvirtual机制
        (*px->vtbl[2])(px);//使用virtual机制扩展px->foo();
        if(px!=0){
            (*px->vtbl[1])(px);//使用virtual调用destructor
            delete px;
        }
        //不用使用named return statement
        return ;
    }

    ------------------------------------------------------------------------------------

    关键词带来的ȝ~~

    struct ?class 在C++中可以؜用,q不是由于用哪一个决定其Ҏ,只是lh的感觉不同Ş了class更像是c++中承的代名?struct更像C中的数据?/font>

    如果需要向C函数传递数据集合可以用struct以保证对象模型的支持

    使用struct完全可以声明各种class的特?/font>

    对象的差?/font>

    1.E序模型procedural model  同c一?/font>

    2.抽象数据cd模型 ADT object-base 抽象指的是一族表辑ּQpublic接口Q的提供Q具体计方法未?/font>

    3.面向对象 oo 有一些彼此相关的模型通过一个抽象的baseclass用以提供公共接口装h

     

    OO中程序员可能需要处理一个未知实体,在执行点之前无法定Q这是由pointers和reference实现的而在ADT中程序员处理的是一个拥有固定单一cd的实体,~译时期已l确定了~

    c++中的多态:1.把derived class的指针{化成一个基cȝ指针 2virtualfunctions 3.dynamic_cast 和typeidq算W?/font>

    多态主要是l由一个共同的接口来媄响类型的装Q这个接口通常被放|在抽象的baseclass中,q个׃n接口是以virtual functions机制来引发的Q可以在执行期间Ҏobject的真正类型解析出到底是哪一个函数实体被调用?/font>

    我们的代码可以避免由于增加某一个特定的derived 实体而改变,只需要新的derivedclass重写q样的接口便可?/font>

    //试指针引用的多?br>#include <iostream>
        using namespace std;
        struct Point{
        public:
            Point(float x=0.):_x(x){}
            float x(){return _x;}
            void x(float xval){_x=xval;}
            virtual void  out(){
                cout<<"I am a Point"<<endl;
            }
        protected:
            float _x;
        };
        struct Point2d:public Point{
        public:
            Point2d(float x=0.,float y=0.):Point(x),_y(y){}
            float y(){return _y;}
            void y(float yval){_y=yval;}
            virtual void out(){
                cout<<"I am a Point2d"<<endl;
            }
        protected:
            float _y;
        };
        int main(){
            Point pt;
            Point2d pt2;
            Point* ptr;
            Point2d* ptr2;
            pt.out(); pt2.out();
            pt=pt2;pt.out();
            ptr=&pt2;
            ptr->out();
            (*ptr).out();
            Point& ptref=pt2;
            ptref.out();
        }

    output:

    I am a Point
    I am a Point2d
    I am a Point
    I am a Point2d
    I am a Point2d
    I am a Point2d

    指针和引用可以实现多态,因ؓ指针和引用的赋值只是改变其指向的范_q没有触及对象模型的改变Q因此可以生多态的效果。而value的赋值操作会D对象模型的切Ԍ从而真实的改变了对象的内部模型与类型,失去了多态的效果。oo 不支持对对象的直接处?/font>

     

    指针的类型:

    不同的指针从内存角度来看没什么不同,只是占据了一个word的空_但是指针的类型会告诉~译器如何解释某个特定地址中的内存内容以及大小

     

    OB设计也就是ADT设计比OO讄更有效率因ؓ其所有函数引发操作均在编译器定OO需要一些运行时定Q对象构v来不需要设|virtual机制

    比OO紧凑因ؓ无需支持virtual机制从而没有额外负?/font>

    但是OB相对来说Ҏ较~~

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     



    rikisand 2010-03-03 13:58 发表评论
    ]]>
    More Effective C++ (1)http://www.shnenglu.com/zqsand/archive/2010/01/18/105931.htmlrikisandrikisandMon, 18 Jan 2010 06:16:00 GMThttp://www.shnenglu.com/zqsand/archive/2010/01/18/105931.htmlhttp://www.shnenglu.com/zqsand/comments/105931.htmlhttp://www.shnenglu.com/zqsand/archive/2010/01/18/105931.html#Feedback0http://www.shnenglu.com/zqsand/comments/commentRss/105931.htmlhttp://www.shnenglu.com/zqsand/services/trackbacks/105931.htmlW四?nbsp; 效率

    ······条款16 C80-20准则

    大约20%的代码用了80%的资源,E序的整体性能是由该程序的一部分代码所军_的~

    可行的办法是使用E序分析器(profilerQ来扑ֈD性能瓉的拿20%的程序~

    而且要针寚w成瓉的资源来使用相应的分析器~

     

    ······条款17  考虑使用延迟计算

    延迟计算Q?也就是说知道E序要求l出l果的时候才q行q算~ 很好理解Q和操作pȝ中的cow copy on write 一个原理~

    四个使用场景Q?/font>

    ~1~ 引用计数 Q?/font>

      class String{…};

    String s1 = “hello”;

    String s2 = s1 ;      //call string Copy ctor

    通常情况下,s2赋值后会有一个hello的拷贝,者通常需要用new操作W分配内存,之后strcpys1

    的数据给他,但如果下面的操作如下的话Q?/font>

    cout << s1 ;

    cout << s1 + s2;

    q种情况下如果只增加s1的引用计敎ͼ而s2只是׃ns1的值就可以了。只有在需要对s2q行修改或者s1q行修改Ӟ才需要真正拷贝给s2一个副本,引用计数的实现在29条款

    ~2~区分d操作

    如: String s = “homer’s all”;

    cout<< s[3];

    s[3] = ‘x’;

    在进行读操作Ӟ使用引用计数是开销很小的,然而写操作必须生成新的拯。通过条款30的代理类我们可以把判断读写操作推q到我们能够军_哪个是正操作的时?/font>

    ~3~延迟d

    假设E序使用了包含许多数据成员的大对象,q些对象必须在每ơ程序运行的时候保留下来,因此存进了数据库。某些时候从database中取出所有数据是没有必要的,比如他只讉K该对象中的一个数据成员。此Ӟ应该对对象进行处理,只有对象内部某一个特定的数据成员被访问的时候才把他取出来。类gos中的按需换页~

    class LargeObject{

        LargeObject(ObjectID id);

    const string& field1() const;

    int field2() const;

    double field3() const;

    const string& field4() const;

    private:

    ObjectID id;

    mutable string* field1value;

    mutable int   * fieldValue;

    };

    LargeObject::LargeObject(ObjectID id):oid(id),fieldValue(0),…{}

    const string& LargeObject::field1()const{

       if(fieldValue == 0){

           //read the data for field 1 from database and make field1 point to it

       }

       return  *field1Value;

    }

    实施lazy fetching M成员函数都需要初始化I指针以指向有效数据。但是const成员函数中,试图修改数据~译器会报错。所以声明字D|针ؓ mutable Q表CZQ何函数都可以修改,即便在const成员函数中也可以~ 条款28中的指针可以让这一Ҏ更灵z?/font>

    ~3~延迟表达式求?/font>

    数D领域,也在使用延迟计算。例?/font>

    matrix<int> m1(1000,1000);

    matrix<int> m2(1000,1000);

    matrix<int> m3 = m1 + m2;

    如果此时计算出m3的话q算量非怹大~

    但是如果此时E序为:

    m3 = m4*m1;

    那么刚才的计就没必要了

    如果cout<< m3[4];

    我们只需要计m3[4]可以了Q其他的值等到确实需要他们的时候才予以计算~如果q气够好的话永远不需要计~

    ȝQgq计只有当软g在某U程度下可以被避免时候才有意义~只有延迟计算得到的好处大于设计它与实现它p的精力时才有意义~

    ·······条款18Q?分期摊还预期的计开销

    提前计算~ over-eager evaluation 在系l要求你做某些事情之前就做了他~

    例如Q大量数据的集合

    template<class NumericalType>

    class DataCollection}{

    public:

      NumericalType min() const;

      NumericalType max() const;

      NumericalType avg() const;

    };

    使用提前计算Q我们随时跟t目前集合的最大最^均|q样 min max avg被调用时候,我们可以不用计算立刻q回正确的数值~~

    提前计算的思想便是Q如果预计某个计会被频J调用,你可以通过设计你的数据l构以更高效的办法处理请求,q样可以降低每次h的^均开销~

    最单的做法?~存已经计算qƈ且很可能不需要重新计的那些值~

    例如在数据库中存有很多办公室的电话号码,E序在每ơ查询电话时先查询本地的~存如果没找到再去访问数据库Qƈ且更新缓存,q样使用~存q_讉K旉要大大减?/font>

    预处理也是一U策略?/font>

    例如设计动态数l的时候,当烦引下标大于已有最大范围时候,需要new出新的空_如果甌两倍于索引的大的话就可以避免频繁的申h作~~~

     

    ········条款 19 Q?了解临时对象的来?/font>

    如果一个对象被创徏Q不是在堆上Q没有名字,那么q个对象是临时对象?/font>

    通常产生于: Z使函数调用能够成功而进行的隐式转换Q或者函数返回对象是q行的隐式{换。由于构造和析构他们带来的开销可以l你的程序带来显著的影响Q因此有必要了解他们~

    ~1首先考虑Z函数调用能通过产生的时对象的情况

    传给某个函数的对象的cd和这个函数所l定的参数类型不一致的情况下会出现q种情况?/font>

    例如Q?/font>

    size_t count(const string& str,char ch);

    函数定义str中ch的数?/font>

    char buffer[100];

    cout<<count(buffer,‘c’);

    传入的是一个char数组Q此时编译器会调用str的构造函敎ͼ利用buffer来创Z个时对象?/font>

    在调用完countChar语句后这个时对象就被自动销毁了~

    仅当传值或者const引用的时候才会发生这Lcd转换~当传递一个非帔R引用的时候,不会发生?/font>

    void uppercasify(string& str); //change all chars in str to upper case;

    在这个例子中使用char数组׃会成功~

    因ؓE序作者声明非帔R引用也就是想让对引用的修改反映在他引用的对象w上Q但是如果此时生成了临时对象Q那么这些修改只是作用在临时对象w上Q也׃是作者的本意了。所以c++止非常量引用生时对象?/font>

    ~2 函数q回对象时候会产生临时对象

    例如Q?const Number operator + ( const Number& lhs,const Number& rhs);

    q个函数q回一个时对象,因ؓ他没有名字,只是函数的返回倹{?/font>

    条款20?Q会介绍让编译器对已l超出生存周期的临时对象q行优化

     

    ········条款20Q?协助~译器实现返回g?/font>

    q回g化:q回带有参数的构造函数?/font>

    cosnt Rational operator * (cosnt Rational& lhs,const Rational& rhs){

        return Rational(lhs.numerator()*rhs.numerator(),lhs.denomiator()*rhs.denominator()};

    c++允许~译器针对超出生命周期的临时对象q行优化。因此如果调用Rational c=a*bQ?/font>

    c++允许~译器消除operator*内部的时变量以及operator*q回的时变量,~译器可以把return表达式所定义的返回对象构造在分配lc的内存上。如果这样做的话那么调用operator*所产生的时对象所带来的开销是0~ 我们可以把operator 声明为内联函数而去除调用构造函数带来的开销~

    #include <iostream>
    #include <string>
    #include "time.h"
    using namespace std;
    char buffer[100];
    class number{
    public:
        const friend  number operator * (const number& rhs,const number lhs);
        number(){}
        number(int b):a(b){}
        number(const number& rhs){
            a = rhs.a;
        }
          int a;
    };
    const number operator*(const number& rhs,const number lhs){
        number res;
        res.a = rhs.a * lhs.a;
            return res;
        /*return number(rhs.a*lhs.a);*/
    }
    //CLOCKS_PER_SEC
    int main()
    {
        clock_t start = clock();
        number A(5);number B(6);
        for(int i=0;i<100000000;i++)
            number C = A*B;

        clock_t end = clock();
        cout<<double(end-start)/CLOCKS_PER_SEC<<endl;
    }

    通过上面的程序运?如果没有q回g?q行旉 15.9s 优化后是 10.1s

    q是很显著的?快了33% Q如果这U情况出现在E序的热点处~效果很好了

     

    ·········条款21 Q?通过函数重蝲避免隐式cd转换

    例子Q?/font>

    class upint{

    public:

    upint();

    upint(int value);

    };

    cosnt upint operator+(const upint&lhs,const upint&rhs);

    upint up1,up2;

    upint up3 = up1+up2;

    upi3 = up1 +10;

    upi4 = 10+ upi2;

    q些语句也可以通过Q因为创Z临时对象Q通过带有int的构造函C生了临时的upint对象Q如果我们不愿意些时对象的产生与析构付ZP我们需要做什么:

    我们声明 cosnt upint operator+(cosnt upint&lhs,int rhs);

    cosnt upint operator+(int lhs,const upint& rhs);

    可以去除时对象生了~

    但是如果我们写了 const upint operator+(int lhs,int rhs); // 错了~

    c++规定Q每一个被重蝲的运符必须臛_有一个参数属于用戯定义cdQintq不是自定义cd所以上面的不对?/font>

    同样的如果希望string char* 作ؓ参数的函敎ͼ都有理由q行重蝲而避免隐形类型{换(仅仅在有必要的时候,也就是说他们可以对程序效率v到很大帮助的时候~Q?/font>

    ··········条款Q?考虑使用 op = 来取?单独?opq算W?/font>

    class Rational{

    public:

       Rational& operator+=(const Rational& rhs);

       Rational& operator-=(const Rational& rhs);

    }

    const Rational operator+(cosnt Rational& lhs,const Rational & rhs){

        return Rational(lhs)+=rhs;

    }

    利用+= -=来实? -可以保证q算W的赋值Ş式与单独使用q算W之间存在正常的关系?/font>

    Rational a,b,c,d,result;

    result = a+ b+c+d; // 可能要用?个时对?/font>

    result +=a;result+=b;result+=c; //没有临时对象

    前者书写维护都更容易,而且一般来说效率不存在问题Q但是特D情况下后者效率更高更可取

    注意Q?/font>

    如果+的实现是q样的:

    const T operator+ (constT& lhs,const T&rhs){

         T result(lhs);

         return result += rhs;

    }

    q个模版中包含有名字对象resultQ这个对象有名字意味着q回g化不可用~~~~~~~~·

     

    ·······条款23 Q?考虑使用其他{h的程序库

    LQ?/font>

    提供cM功能的程序库通常在性能问题上采取不同的权衡措施Q比如iostream和stdioQ所以通过分析E序扑ֈ软g瓉之后Q可以考虑是否通过替换E序库来消除瓉~~~~

     

     

    ······条款24 : 理解虚函敎ͼ多重l承Q虚基类以及 RTTI 带来的开销

    虚函数表Qvtabs 指向虚函数表的指?vptrs

    E序中每个声明了或者承了的虚函数的类都具有自q虚函数表。表中的各个就是指向虚函数具体实现的指针?/font>

    class c1{

       c1();

       virtual ~c1();

       virtual void f1();

       virtual int f2(char c)const;

       virtual void f3(const string& s);

    };

    c1 的虚函数表包括: c1::~c1 c1::f1 c1::f2 c1::f3

    class c2:public c1{

       c2();

       virtual ~c2();

       virtual void f1();

       virtual void f5(char *str);

    };

    它的虚函数表入口指向的是那些由c1声明但是c2没有重定义的虚函数指针:

    c2::~c2  c2::f1 c1::f2 c1::f3 c2::f5

    所以开销上: 必须为包含虚函数的类腑և额外的空间来存放虚函数表。一个类的虚函数表的大小取决于它的虚函数的个敎ͼ虽然每一个类只要有一个虚函数表,但是如果有很多类或者每个类h很多个虚函数Q虚函数表也会占据很大的I间Q这也是mfc没有采用虚函数实现消息机制的一个原因?/font>

    ׃每一个类只需要一个vtbl的拷贝,把它攑֜哪里是一个问题:

    一U:为每一个需要vtbl的目标文件生成拷贝,然后q接时取出重复拷?/font>

    或者:更常见的是采用试探性算法决定哪一个目标文件应该包含类的vtbl。试探:一个类的vtbl通常产生在包含该cȝ一个非内联Q非U虚函数定义的目标文仉。所以上面c1cȝvtbl放在c1::~c1 定义的目标文仉。如果所有虚函数都声明ؓ内联Q试探性算法就会失败,在每一个目标文件就会有vtbl。所以一般忽略虚函数的inline指o?/font>

    如果一个类h虚函敎ͼ那么q个cȝ每一个对象都会具有指向这个虚函数表的指针Q这是一个隐藏数据成员vptr~被编译器加在某一个位|?/font>

    此处W二个开销Q你必须在每一个对象中存放一个额外的指针~

    如果对象很小q个开销十分显著~~因ؓ比例大~

    此时 void makeCall(c1* pc1){

       pc1->f1();

    }

    译?(*pc1->vptr[i])(pc1);

    Ҏvptr扑ֈvtbl q很单,

    在vtbl扑ֈ调用函数对应的函数指针,q个步骤也很单,因ؓ~译器ؓ虚函数表里的每一个函数设|了唯一的烦?/font>

    然后调用指针所指向的函数~

    q样看来Q调用虚函数与普通函数调用的效率相差无几Q只多出几个指o?/font>

    虚函数真正的开销与内联函数有关~Q在实际应用中,虚函C应该被内联,因ؓ内联意味着在编译时ȝ被调用函数的函数体来代替被调用函数。但是虚函数意味着q行时刻军_调用哪个一函数Qso~~~虚函C出的W三个代价啊Q~不能内联Q通过对象调用虚函数的时候,q些虚函数可以内联,但是大多数虚函数通过指针或者以用来调用的)?/font>

    ~多重l承的情?/font>

    多重l承一般要求虚基类。没有虚基类Q如果一个派生类h多个通向基类的承\径,基类的数据成员会被复制到每一个承类对象里,l承cM基类间的每一条\径都有一个拷贝?/font>

    有了虚基c,通常使用指向虚基cȝ指针作ؓ避免重复的手D,q样需要在对象内部嵌入一个或者多个指针~也带来了一定的开销~

    例如菱Şl承 Q?

    class A{};

    class B:virtual public A{};

    class C:virtual public A{};

    class D:public B,public C{};

    q里A是一个虚基类Q因为B和C虚拟l承了他?/font>

    对象 D 的布局Q?/font>

    B data

    vptr

    pointer to virtual base class

    C data

    vptr

    pointer to virtual base class

    D data members

    A data members

    vptr

    上面四个c,只有三个vptrQ因为B和D可以׃n一个vptr  Qؓ啥?Q?/font>

    现在我们已经看到虚函数如何对象变得更大Q以及ؓ何不能把它内联了~

    下面我们看看RTTI的开销 runtime type identifycation 所需要的开销

    通过rtti我们可以知道对象和类的有关信息,所以肯定在某个地方存储了这些供我们查询的信息,q些信息被存储在type_info cd的对象里Q你可以通过typeidq算W访问一个类的type_info对象?/font>

    每个cM仅需要一个RTTI的拷贝,规范上只保证提供哪些臛_有一个虚函数的对象的准确的动态类型信息~

    whyQ和虚函数有啥关p~ 因ؓrtti设计在vtbl?/font>

    vtbl的下?包含指向type_info对象的指针。所以用这U实现方法,消费的空间是vtbl中占用一个额外的单元再加上存储type_info对象所需要的I间?/font>

     

    ------------------------|恶的结束线 OVER~------------------------------------------



    rikisand 2010-01-18 14:16 发表评论
    ]]>
    断言的?/title><link>http://www.shnenglu.com/zqsand/archive/2010/01/17/105886.html</link><dc:creator>rikisand</dc:creator><author>rikisand</author><pubDate>Sun, 17 Jan 2010 11:36:00 GMT</pubDate><guid>http://www.shnenglu.com/zqsand/archive/2010/01/17/105886.html</guid><wfw:comment>http://www.shnenglu.com/zqsand/comments/105886.html</wfw:comment><comments>http://www.shnenglu.com/zqsand/archive/2010/01/17/105886.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/zqsand/comments/commentRss/105886.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/zqsand/services/trackbacks/105886.html</trackback:ping><description><![CDATA[ <p>解决目的问题,意识到断a的重要性。如果一个程序在某处遇到了非法的|那么最好的情况便是在此d下报错,最坏的情况便是E序不吭不响的执行着~~直到你发C执行的方式极异,此时Q你要花九牛二虎之力才能扑ֈ错误所在之处~~~~</p> <p>学习一下断a吧:</p> <p><font size="3"><font face="YaHei Consolas Hybrid" color="#008000">·······什么是断言</font></font></p> <p><font face="YaHei Consolas Hybrid" color="#008000" size="3">在某处判断某一个表辑ּ的gؓ真或者假Q如果假则输出错误消息ƈ停止E序的执行~</font></p> <p><font face="YaHei Consolas Hybrid" color="#008000" size="3">assert是宏Q而不是函敎ͼ只在debug版本中有效,因此无需在release版本删除?/font></p> <p><font face="YaHei Consolas Hybrid" color="#008000" size="3">·······哪几U断a</font></p> <p><font face="YaHei Consolas Hybrid" color="#008000" size="3">MFC </font></p> <p><font face="YaHei Consolas Hybrid" color="#008000" size="3">ASSERT</font></p> <p>void foo(char* p,int size)<br>{<br>ASSERT(p != 0); // 验证~冲区指?br>ASSERT((size >= 100); // 认~冲区大至ؓ100字节<br>// foo 函数的其它计过E?br>}<br>如果没有定义_DEBUG预处理符Q则该语句不会真正生成代码。Visual C++会在调试模式~译时自动定义_DEBUGQ而在发行模式下,该预处理W是不存在的。如果定义了_DEBUGQ则上述两个断言生成的代码类如: <br>//ASSERT(p != 0);<br>do<br>{<br>if(!(p != 0) && AfxAssertFailedLine(__FILE__, __LINE__))<br>AfxDebugBreak();<br>} while(0);<br>//ASSERT((size >= 100);<br>do<br>{<br>if(!(size >= 100) && AfxAssertFailedLine(__FILE__,__LINE__))<br>AfxDebugBreak();<br>}while(0); </p><p><font face="YaHei Consolas Hybrid" color="#008080" size="3">ASSERT_KINDOF(classname,pObject); ASSERT_KINDOF(CDocument,pDocument);</font></p> <p><font face="YaHei Consolas Hybrid" color="#008080" size="3">验pObject指向的对象是classnamecȝ一个对象或者其zcȝ对象</font></p> <p><font face="YaHei Consolas Hybrid" color="#008080" size="3">ASSERT_VALID(pObject); pObject 必须是一个派生于CObjectcȝcd象,会调用其重写的AssertValid函数 Q例?/font></p> <p>如果使用应用向导或类向导生成ZMFC的类Q通常会得到AssertValid()的骨Ӟ最好改写这些骨架代码以增加最基本的完整性检查。下面是一个典型的例子Q类Sample从CObjectl承Q假定它含有职员名字及其薪水Q?<br>class Sample : public CObject<br>{<br>    protected:<br>    CString m_Name; // 职员名字<br>    double m_Salary; // 薪水<br>public:<br>    Sample(LPCTSTR name,double salary) : m_Name(name), m_Salary(salary) {}<br><em>   #ifdef _DEBUG<br>        virtual void AssertValid() const;<br>    #endif</em><br>};<br><em>#ifdef _DEBUG<br>void Sample::AssertValid() const<br>{<br>    CObject::AssertValid(); // 验证基类<br>    ASSERT(!m_Name.IsEmpty()); // 验证职员名字<br>    ASSERT(m_Salary > 0); // 验证薪水<br>}<br>#endif</em> </p><p><font face="YaHei Consolas Hybrid" color="#408080" size="3">CRT assertion</font></p> <p><font face="YaHei Consolas Hybrid" color="#408080" size="3">_ASSERT ?nbsp; _ASSERTE 后一个会在出错时同时打印出条件判断句</font></p> <p><font face="YaHei Consolas Hybrid" color="#408080" size="3">ANSI</font></p> <p><font face="YaHei Consolas Hybrid" color="#408080" size="3">assert()</font></p> <p><font face="YaHei Consolas Hybrid" color="#408080" size="3">注意Qassert用于非法的输入Q但是合法的输入q不一定是正确的,例如Q?/font></p> <p><font face="YaHei Consolas Hybrid" color="#408080" size="3">int pB = (int*)malloc(sizeof(int)*1000);</font></p> <p><font face="YaHei Consolas Hybrid" color="#408080" size="3">assert(pB!=NULL) //错误的用assert 他会在release版本失效~也就是说assert不应该对E序产生副作?/font></p> <p><font face="YaHei Consolas Hybrid" color="#408080" size="3">正确的做法:</font></p> <p><font face="YaHei Consolas Hybrid" color="#408080" size="3">int pB = (int*) malloc(sizeof(int)*1000);</font></p> <p><font face="YaHei Consolas Hybrid" color="#408080" size="3">if(pB == NULL)</font></p> <p><font face="YaHei Consolas Hybrid" color="#408080" size="3">{<br>   //错误处理</font></p> <p><font face="YaHei Consolas Hybrid" color="#408080" size="3">}</font></p> <p><font face="YaHei Consolas Hybrid" color="#408080" size="3">else{</font></p> <p><font face="YaHei Consolas Hybrid" color="#408080" size="3">}</font></p> <p><font face="YaHei Consolas Hybrid" color="#408080" size="3">另一个例子:</font></p> <p><font face="YaHei Consolas Hybrid" color="#408080" size="3">void draw(){</font></p> <p><font face="YaHei Consolas Hybrid" color="#408080" size="3">   CFigure* pF = getCF();</font></p> <p><font face="YaHei Consolas Hybrid" color="#408080" size="3">   assert(pf!=NULL);</font></p> <p><font face="YaHei Consolas Hybrid" color="#408080" size="3">   if(pf == NULL){}</font></p> <p><font face="YaHei Consolas Hybrid" color="#408080" size="3">   else{</font></p> <p><font face="YaHei Consolas Hybrid" color="#408080" size="3">   }</font></p> <p><font face="YaHei Consolas Hybrid" color="#408080" size="3">}</font></p> <p><font face="YaHei Consolas Hybrid" color="#408080" size="3">此处Q对于getCF来说q回gؓNULL是非法的Q如果他的返回值可能ؓnull没必要加上assert语句?/font></p> <p><font face="YaHei Consolas Hybrid" color="#408080" size="3">而下面的if语句则是Z防止release版本出现null指针的情c?</font></p> <p><font face="YaHei Consolas Hybrid" size="3"></font><font color="#408080"> </font></p> <p><font face="YaHei Consolas Hybrid" size="3"></font><font color="#408080"> </font></p> <p><font face="YaHei Consolas Hybrid" color="#408080" size="3">VERIFY()</font></p> <p><font color="#408080">׃ASSERT仅在E序的调试版起作用,试表达式L被动的。也是_它们不能包含赋倹{增量、减量等真正改变数据的操作。但有时候我们需要验证一个主动表辑ּQ比如赋D句。这时可以用VERIFY代替ASSERT。下面是一个例子: <br>void foo(char* p,int size)<br>{<br>char* q; // 指针的副?br><em>VERIFY(q = p);</em> // 拯指针q执行验?br>ASSERT((size >= 100); // 保~冲区大至ؓ100字节<br>// 执行 foo 的其它操?br>}<br>在调试模式下ASSERT和VERIFY是相同的。但在release模式下,VERIFY能够l箋对表辑ּ求|但不再进行断a验)Q而ASSERT语句在效果上如同已l删除了一栗?<br>管在MFC源代码中可以扑ֈ一些应用VERIFY的例子,但ASSERT用得更ؓ普遍。一些程序员L完全避免使用VERIFYQ因Z们已l习惯于使用被动断言。请CQ如果在ASSERT语句中用了d表达式,~译器不会发ZQ何警告。在发行模式下编译时该表辑ּ会被直接删除Q从而导致程序运行的错误。由于发行版E序不含调试信息Q这U类型的错误是很难找到原因的?</font> </p><p><font face="YaHei Consolas Hybrid" size="3"></font><font color="#408080"> </font></p> <p><font face="YaHei Consolas Hybrid" color="#004080" size="3"></font> </p> <p><font face="YaHei Consolas Hybrid" color="#004080" size="3"></font> </p> <p><font face="YaHei Consolas Hybrid" color="#004080" size="3"></font> </p> <p><font face="YaHei Consolas Hybrid" color="#004080" size="3"></font> </p> <p><font face="YaHei Consolas Hybrid" color="#004080" size="3"></font> </p> <p><font face="YaHei Consolas Hybrid" color="#004080" size="3"></font> </p> <p><font face="YaHei Consolas Hybrid" color="#004080" size="3"></font> </p> <p><font face="YaHei Consolas Hybrid" color="#004080" size="3"></font> </p> <p><font face="YaHei Consolas Hybrid" color="#004080" size="3"></font> </p> <p><font face="YaHei Consolas Hybrid" color="#004080" size="3"></font> </p> <p><font face="YaHei Consolas Hybrid" color="#004080" size="3"></font> </p> <p><font face="YaHei Consolas Hybrid" color="#004080" size="3"></font></p><img src ="http://www.shnenglu.com/zqsand/aggbug/105886.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/zqsand/" target="_blank">rikisand</a> 2010-01-17 19:36 <a href="http://www.shnenglu.com/zqsand/archive/2010/01/17/105886.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>c++ RTTI (1)http://www.shnenglu.com/zqsand/archive/2010/01/03/104714.htmlrikisandrikisandSun, 03 Jan 2010 09:59:00 GMThttp://www.shnenglu.com/zqsand/archive/2010/01/03/104714.htmlhttp://www.shnenglu.com/zqsand/comments/104714.htmlhttp://www.shnenglu.com/zqsand/archive/2010/01/03/104714.html#Feedback0http://www.shnenglu.com/zqsand/comments/commentRss/104714.htmlhttp://www.shnenglu.com/zqsand/services/trackbacks/104714.html目中遇C RTTI 先学一部分 c++ primer内容

    1. q行时类型识别:

    E序可以使用基类的指?/font>或者引用来索这些指针或引用所指对象的实际zcd

    标准库提供了两个操作W:

    1. typeid    他可以返回指针或者引用所指向对象的实际类?/font>

    2. dynamic_cast  基cȝ型的指针或引用安全的转ؓzcȝ指针或者引?nbsp;                           

    对于不带虚函数的cd~译时期执行Q否则在q行期间执行

    使用时机Q?/font>

    基类指针调用zcL员函数的话,一般应使用虚函敎ͼq样~译器会Ҏ对象的实际类型来选择正确的函数。但是某些情况下无法使用虚函敎ͼ那么?/font>

    时如果需要用基cL针调用派生类成员函数侉K要用RTTI强制转换Q而且必须查{换是否成?/font>?/font>

    (一) Dynamic_cast

    dynamic_cast 如果转换到指针失败则q回 0 如果转换到引用类型失败则抛出 bad_cast 异常

    Ҏ针操作:

    if(Derived *derivedPtr = dynamic_cast<Derived*> (basePtr)){

        //basePtr point to a derived object;

    }else{

       //basePtr point to a base object;

    }

    ?if 语句中进行检?1.条g代码内部知道 derivedPtr 的类?2.不可能在试代码和用代码中加入代码Q也是说不会在没有试前就使用derivedPtr 3.如果p|外部不会使用未绑定的指针derivedPtr

    对引用操作:

    因ؓ不存在空引用所以{换失败抛出异?/font>

    void f(const Base& b){

        try{

            const Derived &d = dynamic_cast<const Derived&> (b);

        }catch(bad_cast){

        }

    }

    (? typeid

    typeid(e) e是Q意的表达式或者类型名

    Base *bp;

    Derived *dp;

    //compare type at run time of two objects

    if(typeid(*bp)==typeid(*dp)){

        //bp dp point to objects of the same type

    }

    if(typeid(*bp)==typeid(Derived)){

        //bp actually point to a Derived

    }

    注意typeid 试的是 *bp 对象

    //test always fails: The type of bp if pointer to Base

    if(typeid(bp)==typeid(Derived)){

    }

    Base* 永q和Derivedcd不同

    只有typeid 的操作数是带虚函数的cȝ对象的时候,才返回动态类型信息,试指针q回指针的静态的Q编译时cd

    Q三 Qtype_info c?/p>

    作ؓtypeid的返回?提供

    t1==t2 t1!=t2 t.name() 的操?/p>

     

    作ؓ应用例子Q可以实C个类型敏感的相等操作W?/p>

    friend bool operator == (const base& lhs, const base& rhs){

        return typeid(lhs)==typeid(rhs) && lhs.equal(rhs);

    }

    q样可以处理基类子类混合的相{判断了



    rikisand 2010-01-03 17:59 发表评论
    ]]>
    TC-Practice-Recordhttp://www.shnenglu.com/zqsand/archive/2009/12/26/104143.htmlrikisandrikisandSat, 26 Dec 2009 09:28:00 GMThttp://www.shnenglu.com/zqsand/archive/2009/12/26/104143.htmlhttp://www.shnenglu.com/zqsand/comments/104143.htmlhttp://www.shnenglu.com/zqsand/archive/2009/12/26/104143.html#Feedback0http://www.shnenglu.com/zqsand/comments/commentRss/104143.htmlhttp://www.shnenglu.com/zqsand/services/trackbacks/104143.htmltchs-1 none 1000pt DFS 利用q入的方向划分四个边

    tchs-2 250pt 直接就?我写??500pt 暴力可以q,但是判断时候不能用stringstream 用算术判?也可以用构造法 1000pt 每一位有三种可能?/p>

               不用Q保持不动,变化Q分别递归计算valueq更新结果即可,׃递归深度最多只?3层所以不会tle

               另外也可以写出基Cؓ3的@环来遍历每一U情况具体看代码

        for(i=0,A[0]++;A[i]>2;i++){
           A[i]=0;A[i+1]++;
      }

    tchs-3 1000pt 要想使乘U最大,需要更多的3卛_ 500pt 又看错题?~~~ft 要注意题目一定要看清?/p>

    tchs-4 500pt 模拟题,好难?音乐的~ 可以都乘?6 用整数来计算 点会很烦~ q种题思\要清?一步一步来

    tchs-5 250pt 单题Q注意用double 可以?.0*int׃用double()了还?int(h+1e-9);

              500pt 单题Q把所有word提取出来然后排序Q再依次插入标点卛_Q注意有些小技?/p>

    Code Snippet
         string wordSort(string s)
          {
                vector<string> SA,SB;
                string A="",B="";
                for(int i=0;i<s.size();i++)
                    if(s[i]>='A'&&s[i]<='Z'||(s[i]<='z'&&s[i]>='a')){
                        if(B!=""){
                          SB.push_back(B);B="";
                        }
                        A+=s[i];
                    }
                    else{
                        if(A!=""){
                          SA.push_back(A);A="";
                        }
                        B+=s[i];
                    }
                if(A!="")SA.push_back(A);if(B!="")SB.push_back(B);
                sort(SA.begin(),SA.end());string res="";
                int i=0;
                for(; i<SA.size()&&i<SB.size();i++)
                    if(s[0]>='A'&&s[0]<='Z'||(s[0]<='z'&&s[0]>='a'))
                        res=res+SA[i]+SB[i];
                    else
                        res=res+SB[i]+SA[i];
                for(;i<SA.size();i++)res+=SA[i];
                for(;i<SB.size();i++)res+=SB[i];
                return res;
          }

    思\要清晎ͼ两个轮替记录卛_

                    1000pt    昄的BFS 利用队列 只是题意不太好理解,最好把判断写成函敎ͼȝ序会看v来比较清晎ͼ不容易出错~ 一步一步来



    rikisand 2009-12-26 17:28 发表评论
    ]]>
    ALGORITHM IN C (1)http://www.shnenglu.com/zqsand/archive/2009/11/11/100725.htmlrikisandrikisandWed, 11 Nov 2009 12:18:00 GMThttp://www.shnenglu.com/zqsand/archive/2009/11/11/100725.htmlhttp://www.shnenglu.com/zqsand/comments/100725.htmlhttp://www.shnenglu.com/zqsand/archive/2009/11/11/100725.html#Feedback0http://www.shnenglu.com/zqsand/comments/commentRss/100725.htmlhttp://www.shnenglu.com/zqsand/services/trackbacks/100725.htmlSteps to developing a usable algorithm.
    • Define the problem.
    • Find an algorithm to solve it.
    • Fast enough?
    • If not, figure out why.
    • Find a way to address the problem.
    • Iterate until satisfied.

     

    主要内容 FIND-UNION ALGORITHM

    是利用q查集来考察q通性的法 。条件N个节点,M对节点,输入每一对节Ҏ候,如果已经q通,则忽略,否则输出接点q更?

    主要介绍三种法Q第一U,QUCK-FIND 利用数组记录每一个联通子图,同一个联通子囄节点数组变量是相同的。因此每d一对就需要遍历N个数l变量考察是否需要更斎ͼ最坏时间MNQ实际上每个子图为高度ؓ2的树;W二U,QUICK-UNION 联通子N要union?仅仅需要将两个root union 因此每个联通子N度变高,所有find root 会消耗一定时_当然无需遍历N?Q最坏时_也就是每ơ均需要从叶子节点d溯求根(q里书中丑־例子好像有问题)也是MNQ第三种也就?QUICK WEIGHT UNION q种Ҏ在两个子树合q的时候,不是单的指定合ƈ{略Q而是l过判断的,把size(相应高度也小Q的合ƈ到size大的里面Q实际上很容易理解就是尽量减树的高度来q求quick-find的效率;

    q而作者写道\径压~,也是q种思想。或者实Cؓ全部压羃Q也是说在q行union操作的时候,所有check的节炚w直接链接到union后的新root下面Q或者half union Q容易实C性能好)每次check时候,如果没有停止loop单的?id[i]=id[id[i]];卛_辑ֈ减少到根距离的效果?

    half union 的主要代码:

    int i,j;
    for(i=p;id[i]!=i;i=id[i]) id[i]=id[id[i]];
    for(j=p;id[j]!=j;j=id[j]) id[j]=id[id[j]];
    if(i==j)continue;
    if(size[i]>size[j]) id[j]=i,size[i]+=size[j];
    else        id[i]=j,size[j]+=size[i];

     

    W一ơ用windows live writer 试试效果哦~~

     

     

     

     

     

     

     

     



    rikisand 2009-11-11 20:18 发表评论
    ]]>
    þѹƵ| ŷҹͽþþ| vĻþ| þˬˬƬav| 9191ƷѾþ| þҹҹݺ2022| ۺϾþۺ| պŮ18վþþƷ| ˾Ʒþ| þ޾ƷƷ| ˹ھƷþþþһ| þƵ| 91Ըߺþþþ| þ99ۺϾƷ| ҹAVëƬþ| ޳ɫ999þվ| ԭƷ99þþƷ66| ҹƵþþþһ | պAVþһ | þþƷһպ| þ¾ƷĻ| ȾþֻоƷ| ɫۺϾþþĻ | ŷɫ۾þþƷ| ޾Ʒ97þĻ| ɫۺϾþ| Ʒþþ| Ļɫ͵͵þ| þѾDzݲƷ| ŷսþþþþþ | þþþƷһ | þ߳ˮ| ޹ۺϾþ| þۺƵվ| ԸߺþþþþþþAAAAA| þþƷһ| þþùƷ| 99þþƷۺһ| þúݺɫۺ| Ѿþþþþ| þùƷҰAV|