分享 C#不用treeview控件生成美貌的树型构造

从未太高的复用性,用到的人活动改良把呵呵。别忘了改后再享受下。个中CategoryInfo类是分类实体类。结合jquerytreeview插件做出的效应图如下:详细参谋:大理网址建设:C#变化美观的树型布局

//例1
class A
{
public:
int var;
void fun(){}
};
//即便如此,结果还是和上边的一样,不生成构造函数!
//因为没有任何理由对var初始化,况且编译器也不知道用什么值给它初始化。
            你要记住的事

www.4688.com 1

正文翻译自modern effective C++,由于水平有限,故无法保障翻译完全精确,迎接提出错误。多谢!

据称是PHPCMS用的浮动树类。的确超级帅,未有太多时光平昔照搬改成C#版。

参考c++什么日期会生成暗中认可布局函数以致《深度研究C++对象模型》

博客已经搬迁到这里啦

C++的法定说法中,特殊成员函数是C++愿意去主动转换的。C++98有4个如此的函数:暗中同意布局函数,析构函数,拷贝结构函数,拷贝operator=。当然,这里有些细则。这几个函数只在要求的时候爆发,也正是,在类中假如有些代码没有理解地宣称它们就动用了它们。二个暗许构造函数唯有在类中一贯不证明任何构造函数的图景下才会被变型出来(当你的指标是需要那一个类的布局函数必得提供参数时,那制止编写翻译器为您的类生成贰个默许构造函数。)。特殊成员函数被隐式生成为public和inline,並且它们是nonvirtual,除非是在派生类中的析构函数,并且那些派生类世袭自带virtual析构函数的基类。在这里种景况下,派生类中,编写翻译器生成的析构函数也是virtual。

只是你已经领会这个业务了。是的,是的,这几个都是古旧的野史了:两河流域,东周,FORTRAN,C++98。不过时间变了,同期C++中国和澳洲常规成员函数的转移法规也改成了。意识到新法则的发出是很主要的,因为没有怎么事和“知道怎么着时候编写翻译器会暗自地把成员函数插入到你的类中”一样能一孔之见飞快C++编制程序的大旨了。

在C++11中,特殊成员函数“俱乐部”有多少个新成员:move构造函数和move operator=。这里给出它们的函数具名:

class Widget{
public:
    ...
    Widget(Widget&& rhs);               //move构造函数

    Widget& operator=(Widget&& rhs);    //move assignment operator
    ...
};

调节它们的改换和表现的准则和它们的“copying兄弟”很像。move操作独有在被须要的时候生成,而且只要它们被变型出来,它们对类中的non-static成员变量实行“memberwise move”(“以分子为单位各种move”)。那代表move构造函数,用参数rhs中的相应成员“移动布局”(move-construct)每个non-static成员变量,并且move operator=“移动赋值”(move-assign)每一种non-static成员变量。move布局函数相像“移动布局”基类的局地(如果存在的话),而且move operator=也“移动赋值”它的基类部分。

近期,当本人提起move操作(移动布局或位移赋值)一个分子变量或基类时,无法保障move会真正发生。“memberwise move”事实上更像二个呼吁,因为那个不是move-enabled(能移动的)类型(也正是,不提供move操作的品种,举例,大大多C++98遗留下来的类)将通过copy操作来“move”。每一种memberwise “move”的重大都以std::move的运用,首先move来自多少个对象(std::move的参数),然后经过函数重载拆解分析来决定执行move或copy,最后产生三个结出(move来的或copy来的)。(The heart
of each memberwise “move” is application of std::move to the object to be moved from, and the result is used during function overload resolution to determine whether a move or a copy should be performed. )Item 23含有了那个进度的细节。在此个Item中,只须求轻巧地记住“memberwise move”是那样运作的:当成员函数和基类扶助move操作时,就选用move,假设不明了move操作,就动用copy。

与copy操作同样,假使您协和注脚了move操作,编写翻译器就不会帮你生成了。可是,它们被变型的具体条件和copy操作有少数不平等。

三个copy操作是单独的:声喜宝(HippState of Qatar个不会阻止编写翻译器生成此外八个。所以黄金时代旦你表明了两个正片布局函数,可是并未有注脚拷贝operator=,然后你写的代码中要用到拷贝赋值,编译器将帮您生成一个拷贝operator=。相仿的,要是你注明了三个拷贝operator=,可是尚未证明拷贝结构函数,然后您的代码要求copy结构,编写翻译器将帮你生成叁个拷贝构造函数。这在C++9第88中学是不利的,在C++11如故不利的。

多少个move操作不是独立的:若是您评释了此外一个,那就截留了编写翻译器生成其余八个。相当于说,基本原理正是,若是您为您的类表明了二个move布局函数,那么你就标注你的move布局函数和编写翻译器生成的例外,它不是因而暗中同意的memberwise move来达成的。而且只要memberwise move构造函数不对的话,那么memberwise move赋值函数也应当不对。所以声美赞臣(Meadjohnson卡塔尔个move结构函数会阻止多个move operator=被自动生成,声美赞臣(Meadjohnson卡塔尔(قطر‎个move operator=函数会阻止二个move布局函数被自动生成。

除此以外,假若其余类显式地声称了一个copy操作,move操作就不会被自动生成。理由是,声美素佳儿个copy操作(构造函数或assignment函数)注解了用健康的主意(memberwise copy)来拷贝对象对于那几个类来讲是不对路的,然后编写翻译器感觉,如若对于copy操作来讲memberwise copy不对劲,那么对于move操作来讲memberwise move很有异常的大希望也是不适用的。

转头也是这么。声美素佳儿(Aptamil卡塔尔(قطر‎个move操作会使得编写翻译器让copy操作不可用(通过delete(看Item 11)能够使得copy操作不可用。)总来讲之,假诺memberwise move不是move一个目的最合适的法子,就从未理由希望memberwise copy是copy那么些目的的伏贴方式。那听上去大概会毁掉C++98的代码,因为比起C++98,在C++11中让copy操作可行的节制条件要更加多,可是动静不是如此的。C++98的代码未有move操作,因为在C++9第88中学向来不和“moving”贰个目的同样的政工。遗留的类唯少年老成能有所二个user-declared(顾客自身注解的)move操作的不二诀要是它们被增加到C++1第11中学,并且使用move语义来修改这几个类,这样这些类才必得遵守C++11的不成方圆来变化新鲜成员函数(也正是防止copy操作的成形)。

莫不你早就听过被称呼“三法则”(“the Rule of Three”)的准绳了。三准则评释了假设您表明了别的三个正片布局函数,拷贝operator=或析构函数,那么你应有评释全体的那八个函数。它产生于四个着重(自定义copy操作的需求大致都来源于少年老成体系,这种类供给对部分能源扩充管理),何况大多数暗中提示着:(1)在四个copy操作中做的其余财富管理,在另三个copy操作中很恐怕也急需做相似的军事拘留。(2)类的析构函数也须要参加财富管理(日常是自由能源)。必要被管理的优异能源便是内存了,而且那也是干什么全部管理内部存款和储蓄器的科班库类(例如,推行动态内部存款和储蓄器管理的STL容器)都被称作“the big three”:两个copy操作和五个析构函数。

三法则的八个定论是:类中现身三个user-declared析构函数表示简单的memberwise copy大概不太符合copy操作。那反过来就提出:借使三个类表明了二个析构函数,copy操作大概不应有被自动生成,因为它们或者将作出一些不正确的事。在C++98被应用的时候,这几个缘故的重要未有被开采,所以在C++98中,user-declared析构函数的存在不会潜移暗化编写翻译器生成copy操作的意愿。这种意况在C++11中依然存在的,不过那只是因为口径的限定(如若阻止copy操作的生成会破坏太多的遗留代码)。

而是,三准绳悄悄的因由只怕有效的,并且,结合从前的考查(copy操作的宣示阻止隐式move操作的扭转) ,那促使C++11在二个类中有一个user-declared的析构函数时,不去变通move操作。

故此只在下边那多少个业务为确实时候才为类生成move操作(当须要的时候):

在一些情状下,相符的平整大概延伸到copy操作中去,因为当叁个类中宣称了copy操作依旧一个布局函数时,C++11不赞成自动生成copy操作。那象征风度翩翩旦您的类中,已经宣示了析构函数可能个中一个copy操作,不过你依赖于编译器帮您转移此外的copy操作,那么你应当“进级”一下那个类来撤消注重。就算编写翻译器生成的函数提供的行为是不利的(也正是,假使memberwise copy正是您想要的),你的做事就极粗略了,因为C++11的“=default”让你能鲜明地宣称:

class Widget {
public:
    ...
    ~Widget();                  //user-declared析构函数

    ...
    Widget(const Widget&) = default;    //默认的拷贝构造函数的行为OK的话

    Widget&
        operator=(const Widegt&) = default; //默认的行为OK的话
    ...
};

这种情势在多态基类(也正是,定义“派生类对象急需被调用的”接口的类)中常常是实用的。多态基类平日具有virtual析构函数,因为假若它们从不,一些操作(比方,通过指向派生类对象的基类指针实行delete操作或基类引用进行typeid操作(译注:typeid操作只要基类有虚函数就不会错,最重点的来由或许析构函数的delete))会发生未定义或不当的结果。除非这么些类世襲了二个已然是virtual的析构函数,而唯生龙活虎让析构函数成为virtual的方法便是显示申明它。日常,私下认可完成是对的,“=default”正是很好的法子来抒发它。不过,二个user-declared析构函数禁止了move操作的发出,所以只要move的力量是被扶植的,“=default”就找到第三个使用之处了。声美赞臣个move操作会让copy操作失效,所以风姿洒脱旦copy的工夫也是需求的,新风流洒脱轮的“=deafult”能做那样的办事:

class Base{
public:
    virtual ~Base() = default;              //让析构函数成为virtual

    Base(Base&&) = default;                 //支持move
    Base& operator=(Base&) = default;   

    Base(const Base&) = default;            //支持copy
    Base& operator=(const Base*) = default;

    ...
};

实则,尽管你有二个类,编写翻译器愿意为这么些类生成copy和move操作,並且转换的函数的表现是您想要的,你只怕依旧要承当地点的政策(本身评释它们並且选拔“= default”作为定义)。这样须求做越来越多的专门的学业,然则它使得你的思考看起来更清楚,况且它能帮你
躲过一些很微妙的乖谬。比如,假若你有三个类代表三个string表格,相当于贰个数据构造,它同意用叁个整形ID来快捷查看string:

class StringTable{
public:
    StringTable() {}
    ...                     //插入,删除,查找函数等等,但是没有
                            //copy/move/析构函数

private:
    std::map<int, std::string> values;
};

假如这一个类未有证明copy操作,move操作,以致析构函数,这样编写翻译器就能够自动生成那么些函数假如它们被运用了。那样十三分有利。

不过若是过了生龙活虎段时间后,大家以为记录暗中认可结构函数以致析构函数会很有用,并且增加那样的效益也很简短:

class StringTable{
public:
    StringTable() 
    { makeLogEntry("Creating StringTable object");}     //后加的

    ~StringTable()
    { makeLogEntry("Destroying StringTable object");}   //也是后加的

    ...                                                 //其他的函数

private:
    std::map<int, std::string> values;
};

那看起来很合理,可是声可瑞康(Nutrilon卡塔尔(DumexState of Qatar个析构函数有一个要害的隐私副成效:它阻挡move操作被变型。可是copy操作的更换不受影响。因而代码很恐怕会编写翻译通过,试行,况且经过效能测量检验。那包含了move成效的测验,因为就算这几个类中不再有move的力量,不过需要move它是能经过编写翻译而且实践的。那样的央求在本Item的眼下早就认证过了,它会产生copy的调用。那代表代码中“move” StringTable对象实际是copy它们,也正是,copy std::map对象。然后呢,copy一个std::map对象很恐怕比move它会慢好些个少个数据级。由此,轻巧地为类扩大一个析构函数就能推荐三个第朝气蓬勃的属性难点!倘诺早前把copy和move操功能“=default”显式地定义了,那么难题就不会见世了。

最近,已经忍受了笔者上前的啰嗦(在C++11中copy操作和move操作生成的决定法规)之后,你大概会想掌握什么日期笔者才会把集中力放在此外几个奇特成员函数上(暗许布局函数和析构函数)。今后便是时候了,可是唯有一句话,因为那几个分子函数大致平素不改变动:C++11中的准绳差不离和C++9第88中学的准绳相近、

故而C++11对特种成员函数的主宰准则是如此的:

www.4688.com美高梅在线登录网址,留神关于成员函数模板的存在,这里未有法则规定它会阻止编写翻译器生成新鲜成员函数。那象征假设Widget看起来像这么:

class Widget{
public:
    ...
    template<typename T>
    Widget(const T& rhs);               //构造自任何类型

    template<typename T>
    Widget& operator=(const T& rhs);    //赋值自任何类型

    ...
};

不畏那一个template能实例化出拷贝布局函数和拷贝operator=的函数具名(便是T是Widget的情况),编写翻译器依然会为Widget生成copy和move操作(假如以前禁绝它们生成的规范满意了)。在颇有的大概中,那将用作三个逼迫值得分明的边缘景况令你倍感纳闷,不过那是有案由的,笔者以往会谈到它的。Item 26证实了这是有很要紧的由来的。

///summary///通用的树型类,可以生成任何树型结构////summarypublicclassCateTreeView{IListCategoryInfolsCate=newListCategoryInfo();//初始化数组string[]icons=newstring[]{"│","├","└"};//修饰符号stringhtmlret="";stringnbsp="";publicCateTreeView(IListCategoryInfols){lsCate=ls;htmlret="";}publicCategoryInfogetOne(intid){foreach(CategoryInfominlsCate){if(m.cateid==id){returnm;}}returnnull;}///summary///得到父级数组////summary///paramname="myid"/param///returns/returnspublicIListCategoryInfoget_parent(intmyid){IListCategoryInfolsnew=newListCategoryInfo();CategoryInfom=getOne(myid);if(m==null)returnlsnew;intpid=m.parentid;if(pid==0)returnlsnew;pid=getOne(pid).parentid;foreach(CategoryInfomcateinlsCate){if(mcate.parentid==pid){lsnew.Add(mcate);}}returnlsnew;}///summary///得到子级分类////summary///paramname="myid"/param///returns/returnspublicIListCategoryInfoget_child(intmyid){IListCategoryInfolsnew=newListCategoryInfo();foreach(CategoryInfomcateinlsCate){if(mcate.parentid==myid){lsnew.Add(mcate);}}returnlsnew;}///summary///得到树型结构////summary///paramname="myid"表示获得这个ID下的所有子级/param///paramname="str"生成树型结构的基本代码,例如:optionvalue=$id$selected$spacer$nameoption/param///paramname="sid"被选中的ID,比如在做树型下拉框的时候需要用到/param///paramname="adds"/param///paramname="str_group"/param///returns/returnspublicstringget_tree(intmyid,stringstr,intsid,stringadds,stringstr_group){intnumber=1;IListCategoryInfochild=get_child(myid);if(child.Count0){inttotal=child.Count;foreach(CategoryInfominchild){stringj="",k="";if(number==total){j+=icons[2];}else{j+=icons[1];k=adds!=""?icons[0]:"";}stringspacer=adds!=""?adds+j:"";stringselected=m.cateid==sid?"selected":"";stringtempstr="";if(m.parentid==0str_group!="")//?"\$nstr=\"$str_group\";":"\$nstr=\"$str\";";{tempstr=str_group;}else{tempstr=str;}tempstr=tempstr.Replace("$id",m.cateid.ToString());tempstr=tempstr.Replace("$parentid",m.parentid.ToString());tempstr=tempstr.Replace("$select",selected);tempstr=tempstr.Replace("$spacer",spacer);tempstr=tempstr.Replace("$name",m.catename);tempstr=tempstr.Replace("$modelid",m.modelid.ToString());htmlret+=tempstr;stringbspstr=nbsp;get_tree(m.cateid,str,sid,adds+k+bspstr,str_group);number++;//$this-ret.=$nstr;//$nbsp=$this-nbsp;//$this-get_tree($id,$str,$sid,$adds.$k.$nbsp,$str_group);//$number++;}}returnhtmlret;//return$this-ret;}///summary///同上一类方法,jquerytreeview风格,可伸缩样式////summary///paramname="myid"表示获得这个ID下的所有子级/param///paramname="effected_id"需要生成treeview目录数的id/param///paramname="str"末级样式/param///paramname="str2"目录级别样式/param///paramname="showlevel"直接显示层级数,其余为异步显示,0为全部限制/param///paramname="style"目录样式默认filetree可增加其他样式如'filetreetreeview-famfamfam/param///paramname="currentlevel"计算当前层级,递归使用适用改函数时不需要用该参数/param///paramname="recursion"递归使用外部调用时为FALSE/param///returnsHTML/returnspublicstringget_treeview(intmyid,stringeffected_id,stringstr,stringstr2,intshowlevel,stringstyle,intcurrentlevel,boolrecursion){IListCategoryInfochild=get_child(myid);stringeffected="id=\""+effected_id+"\"";stringplaceholder="ullispanclass='placeholder'/span/li/ul";if(!recursion)htmlret+="ul"+effected+"class=\""+style+"\"";foreach(CategoryInfominchild){IListCategoryInfochild2=get_child(m.cateid);stringfolder="";//@extract($a);if(showlevel0showlevel==currentlevel(child2.Count0))folder="hasChildren";//如设置显示层级模式@2011.07.01stringfloder_status=((folder!="")?"class=\""+folder+"\"":"");htmlret+=recursion?"ulli"+floder_status+"id=\""+m.cateid+"\"":"li"+floder_status+"id=\""+m.cateid+"\"";recursion=false;if(child2.Count0){stringnstr=str2;//eval("\$nstr=\"$str2\";");stringtempstr=str2;tempstr=tempstr.Replace("$id",m.cateid.ToString());tempstr=tempstr.Replace("$parentid",m.parentid.ToString());//tempstr=tempstr.Replace("$select",selected);//tempstr=tempstr.Replace("$spacer",spacer);tempstr=tempstr.Replace("$name",m.catename);tempstr=tempstr.Replace("$modelid",m.modelid.ToString());htmlret+=tempstr;if(showlevel==0||(showlevel0showlevelcurrentlevel)){get_treeview(m.cateid,effected_id,str,str2,showlevel,style,currentlevel+1,true);}elseif(showlevel0showlevel==currentlevel){htmlret+=placeholder;}}else{stringnstr=str;stringtempstr=str;tempstr=tempstr.Replace("$id",m.cateid.ToString());tempstr=tempstr.Replace("$parentid",m.parentid.ToString());//tempstr=tempstr.Replace("$select",selected);//tempstr=tempstr.Replace("$spacer",spacer);tempstr=tempstr.Replace("$name",m.catename);tempstr=tempstr.Replace("$modelid",m.modelid.ToString());htmlret+=tempstr;//str+=nstr;}htmlret+=recursion?"/li/ul":"/li";}if(!recursion)htmlret+="/ul";returnhtmlret;}}

2、类的基类提供了私下认可的结构函数。

到此处,大家把编写翻译器为类生成默许构造函数的正当理由解说达成,相信我们应该对布局函数的浮动机会有了二个光景的认知。那七种“正当理由”其实是编写翻译器一定要为类生成暗许布局函数的说辞,《Inside The C++ Object Model》里称这种理由为nontrival的(候sir翻译的很别扭,所以怎么翻译随你呀)。除了那多样情景外,编写翻译器称为trival的,也正是未有要求为类生成暗许布局函数。这里斟酌的布局函数生成准绳的原委是写进C++Standard的,如此看来规范就是“贴合符合规律思维”的生机勃勃套准绳(轻便YY一下),其实本便是这样,编写翻译器不该为了生机勃勃致化做一些尚未供给的干活。
透过对默许布局函数的座谈,相信大家对复制布局函数、赋值运算符重载函数、析构函数的调换时机应该能够活动扩大了。对的,它们遵守着八个最根本的法则:唯有编写翻译器一定要为那么些类生成函数的时候(nontrival),编写翻译器才会真的的变通它。
所以,正如标题所说,大家不用被C++语法中所描述的那个条条框框所“蒙骗”了。的确,相信那个生成法则不会对我们的编制程序带给多大的影响(不会发出错误),可是独有询问它们的骨子里操作,我们才领悟编写翻译器究竟为我们做了哪些,大家才精晓什么样使用C++工夫让它变得更有作用——比方消亡不供给的架会谈设想机制等(借使能够的话)。相信本文对C++自动生成的内容的叙说让无数人推断对象布局函数产生的来因去果,希望本文对您抱有利于。