精华内容
下载资源
问答
  • qt 对象树简介

    2011-11-08 17:36:58
    主要介绍Qt 对象树的一些基本原理。通过调试信息可以很清楚的理解
  • Qt 对象树

    千次阅读 2020-04-08 20:14:55
    作者:billy 版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处 Object Tree Model 先来看一下 QObject 的构造...即每一个 QObject 对象有且仅有一个父对象,但可以有很多个子对象。按...

    作者:billy
    版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处

    Object Tree Model

    先来看一下 QObject 的构造函数:
    在这里插入图片描述
    通过帮助文档我们可以看到,QObject 的构造函数中会传入一个 Parent 父对象指针,children() 函数返回 QObjectList。即每一个 QObject 对象有且仅有一个父对象,但可以有很多个子对象。按照这种形式排列就会形成一个对象树的结构,最上层是父对象,下面是子对象,在再下面是孙子对象,以此类推。

    那么Qt为什么要这么设计呢?或者说这样设计的好处是什么呢?很简单,就是为了方便内存管理。我们在创建 QObject 对象时,提供一个父对象,那么我们创建的这个 QObject 对象会自动添加到其父对象的 children() 列表。当父对象析构的时候,这个子对象列表中的所有对象都会被析构,当析构子对象的时候,会自动从父对象的子对象列表中删除

    这种机制在 GUI 程序开发过程中是相当实用的。有一个很明显的现象就是我们会在窗口中new很多控件,但是却没有delete,因为在父控件销毁时这些子控件以及布局管理器对象会一并销毁。

    值得注意的是,如果在构造时设置父对象为 NULL,那么当前实例不会有父对象存在,Qt 也不会自动析构该实例,除非实例超出作用域导致析构函数被调用,或者用户在恰当时机使用 delete 操作符或者使用 deleteLater 方法。

    在这里插入图片描述

    QWidget

    QWidget 也是 QObject 的子类,所以在 parent 机制上是没有区别的。然而实际使用时,对于 QWidget 和其派生类来说,在内存管理上要稍微复杂一些。因为 QWidget 需要和 QEventLoop 高度配合才能完成工作,我们举个例子来说明一下。

    例如 QWidget 的关闭流程,首先用户点击关闭按钮触发 close() 槽,然后Qt向 widget 发送 QCloseEvent,默认的 QCloseEvent 会将 widget 隐藏起来,也就是hide()。我们可以看到,widget 的关闭实际是将其隐藏,而没有释放内存,虽然我们有时会重写 closeEvent 但也不会手动释放 widget。

    所以需要设置 Qt::WA_DeleteOnClose 属性,那么会在 close 之后接着调用 widget 的析构函数,或者手动 delete

    对象树模型存在的小问题

    众所周知,C++中规定了析构顺序应该按照其创建顺序的相反过程
    。那么问题来了,如果我们先创建了子对象,再创建的父对象,根据上述原理析构的时候先析构父对象,又因为Qt中的对象树自动析构原理,我们析构父对象会自动析构子对象。也就是说, 子对象此时就被析构了,然后代码继续执行,按照顺序还要再析构一次子对象,但是这时候已经是第二次调用 子对象的析构函数了,C++中不允许调用两次析构函数,因此,程序会崩溃。

    示例:

    #include <QApplication>
    #include <QPushButton>
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
    
        QPushButton btn("button");
        QWidget widget;
        btn.setParent(&widget);
        widget.show();
    
        return a.exec();
    }
    
    运行结果:
    关闭 widget 后程序崩溃,没有正常结束
    

    由此我们可以看到,Qt 的对象树机制虽然在内存管理上很方便,但是也会带来一些麻烦,为了避免这些麻烦我们可以这么做:

    1. 先创建父对象再创建子类对象,并且在创建子对象时就指定父对象
    2. 尽量在堆上创建子对象

    更多内容请参考 Qt中的内存泄漏

    展开全文
  • Qt对象树与生命期

    千次阅读 2018-10-11 16:58:29
    Qt对象树与生命期 本文要求对C++虚函数的语法比较熟悉,若不熟悉可参阅《C++语法详解》一书,电子工业出版社出版 为什么要使用对象树:GUI程序通常是存在父子关系的,比如一个对话框之中含有按钮、列表等部件,...

    Qt对象树与生命期

    本文为原创文章,转载请注明出处,或注明转载自“黄邦勇帅(原名:黄勇)

    本文出自本人原创著作《Qt5.10 GUI完全参考手册》网盘地址:
    https://pan.baidu.com/s/1iqagt4SEC8PUYx6t3ku39Q
    《C++语法详解》网盘地址:https://pan.baidu.com/s/1dIxLMN5b91zpJN2sZv1MNg

    若对C++语法不熟悉,建议参阅本人所著《C++语法详解》一书,电子工业出版社出版,该书语法示例短小精悍,对查阅C++知识点相当方便,并对语法原理进行了透彻、深入详细的讲解,可确保读者彻底弄懂C++的原理,彻底解惑C++,使其知其然更知其所以然。此书是一本全面了解C++不可多得的案头必备图书。

    为什么要使用对象树:GUI程序通常是存在父子关系的,比如一个对话框之中含有按钮、列表等部件,按钮、列表、对话框等部件其实就是一个类的对象(注意是类的对象,而非类),很明显这些对象之间是存在父子关系的,因此一个GUI程序通常会由一个父对象维护着一系列的子对象列表,这样更方便对部件的管理,比如当按下tab键时,父对象会依据子对象列表令各子对象依次获得焦点。当关闭对话框时,父对象依据子对象列表,找到每个子对象,然后删除它们。在Qt中,对对象的管理,使用的是树形结构,也就是对象树。
    子对象和父对象:本小节的父/子对象是相对于由对象组成的树形结构而言了,父节点对象被称为父对象,子节点对象被称为子对象。注意:子对象并不是指类中的对象成员。

    2.5.1 组合模式与对象树

    组合模式指的是把类的对象组织成树形结构,这种树形结构也称为对象树,Qt使用对象树来管理QObject及其子类的对象。注意:这里是指的类的对象而不是类。把类组织成树形结构只需使用简单的继承机制便可实现。
    使用组合模式的主要作用是可以通过根节点对象间接调用子节点中的虚函数,从而可以间接的对子节点对象进行操作。
    组合模式的基本思想是使用父类类型的指针指向子类对象,并把这个指针存储在一个数组中(使用容器更方便),然后每创建一个类对象就向这个容器中添加一个指向该对象的指针。下面的示例为核心代码

    示例2.12:组合模式核心代码(C++代码)
    
    #include <iostream>
    #include<vector>     //使用容器。
    using namespace std;
    class A {public:  //顶级父类。
    vector<A*> v;  /*创建一个存储父类类型指针的容器,此步也可使用形如A* m[11]的数组代替,但数组不能自动扩容。*/
    	void add(A* ma) { v.push_back(ma);}	};  //add函数的主要作用是将ma添加到容器v中。
    class B :public A {public:	   //需要继承自类A
    	void add(A* ma) { v.push_back(ma);  }		};
    class C :public A {public:  };//需要继承自类A,该类无add函数,也就是说该类的对象不能有子对象。
    
    int main(){	A ma, ma1, ma2, ma3;	B mb, mb1, mb2, mb3;	C mc, mc1, mc2, mc3;
    	//创建对象树。
    	ma.add(&mb);		ma.add(&mb1);		mb.add(&mb3);		mb.add(&mc);		mb.add(&mc1);
    	mb1.add(&mc2);		mb1.add(&ma1);		mb1.add(&ma2);	}
    

    程序的结构如图2-2
    在这里插入图片描述

    示例2.13:打印出组合模式中的各对象的名称(C++代码)
    
    #include <iostream>
    #include <string>
    #include<vector>
    using namespace std;
    class A {public:  
    	string name;//用于存储创建的对象的名称
    	vector<A*> v;  //创建一个存储父类类型指针的容器。
    	A(){}
    A(string n){name=n;}
    	void add(A* ma) { v.push_back(ma);}	  //将ma添加到容器v中。
    	virtual string g(){return name;}   //虚函数,用于获取对象名。
    	void f(){                         //用于显示当前对象的容器v中存储的内容。
    		if(!v.empty())                //若v不为空,则执行以下语句。
    			{cout<<name<<"=";	      //输出当前对象的名称。
    			for (vector<int>::size_type i = 0; i!=v.size(); i++)  //遍历容器v。
    				{cout<<v[i]->g()<<",";}  //输出容器v中存储的对象的名称,注意g是虚函数。
    		cout<<endl;}  }	 //f结束
    	virtual void pt(){ 		      //该函数会被递归调用,用以输出整个对象树中对象的名称。
    		f();
    		for (vector<int>::size_type i = 0; i!=v.size(); i++) 
    			v[i]->pt(); } //注意pt是虚函数,假如v[i]类型为其子类型B时,则会调用B::pt()
    			};        //类A结束
    class B :public A {public:	   //需要继承自类A,代码与类A类似
    	string name;   
    	B(string n){name=n;}
    	void add(A* ma) { v.push_back(ma);  }		
    	string g(){return name;}
    	void f(){
    		if(!v.empty()){cout<<name<<"=";	
    			for (vector<int>::size_type i = 0; i!=v.size(); i++) {cout<<v[i]->g()<<",";}	
    			cout<<endl;}	}   //f结束
    	void pt(){	f();
    		for (vector<int>::size_type i = 0; i!=v.size(); i++) v[i]->pt();	}	};   //类B结束
    class C :public A {public:  //需要继承自类A,该类无add函数,也就是说该类的对象不能有子对象。
    	string name;
    	C(string n){name=n;}
    	void f(){
    if(!v.empty()){cout<<name<<"=";	
    			for (vector<int>::size_type i = 0; i!=v.size(); i++) {cout<<v[i]->g()<<",";}
    			cout<<endl;	}	}  //f结束
    	string g(){return name;}
    	void pt(){	f();
    		for (vector<int>::size_type i = 0; i!=v.size(); i++) v[i]->pt();}	};  //类C结束
    int main()
    {	//创建对象时传递该对象的名称以便存储。
    	A ma("ma"),ma1("ma1"),ma2("ma2"),ma3("ma3"),ma4("ma4");
    	B mb("mb"),mb1("mb1"),mb2("mb2"),mb3("mb3"),mb4("mb4");	
    	C mc("mc"),mc1("mc1"),mc2("mc2"),mc3("mc3"),mc4("mc4");
    	ma.add(&mb);	//ma.v[0]=&mb;
    	mb.add(&mb1);	//mb.v[0]=&mb1;
    	mb.add(&mb2);	//mb.v[1]=&mb2;
    	ma.add(&mb1);	//ma.v[1]=&mb1;
    	mb1.add(&mb3);	//mb1.v[0]=&mb3;
    	mb1.add(&mb4);	//mb1.v[1]=&mb4;
    	mb2.add(&mc1);	//mb2.v[0]=&mc1;
    	mb2.add(&mc2);	//mb2.v[1]=&mc2;
    	mb2.add(&ma1);	//mb2.v[2]=&ma1;
    	cout<<"各对象拥有的子对象"<<endl;
    	ma.f();		mb.f();		mb1.f();		mb2.f();
    	cout<<endl<<"整个对象中结构"<<endl;
    	ma.pt();		}
    

    运行结果及对象的组织结构如图2-3
    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    2.5.2 QObject类、对象树、生命期

    为方便讲解,本文把由QObject及其子类创建的对象称为QObject、QObject对象或Qt对象。在Qt中,QObject对象通常就是指的Qt部件。
    QObject类是所有Qt对象的基类,是Qt对象模型的核心,所有Qt部件都继承自QObject。
    QObject类既没有复制构造函数也没有赋值操作符函数(实际上它们被声明为私有的),因此无法通过值传递的方式向函数传递一个QObject对象。QObject及其派生类的单形参构造函数应声明为explicit,以避免发生隐式类型转换。
    Qt库中的QObject对象是以树状结构组织自已的,当创建一个QObject对象时,可以为其设置父对象,新创建的对象会被加入到父对象的子对象列表中(可通过QObject::children()函数查询),因为Qt的部件类,都是以QObject为基类,因此,Qt的所有部件类都具有对象树的特性。
    1、对象树的组织规则如下
     每一个QObject对象只能有一个父QObject对象,但可以有任意数量的子QObject对象。比如

    A ma;  B mb;   C mc;
       ma.setParent(&mb);    //将对象ma添加到mb的子对象列表中,
       ma.setParent(&mc);    //该语句会把ma从mb的子对象列表中移出,并将其添加到mc的子对象列表中。setParent函数见后文。
    

     QObject对象会把指向各个子对象地址的指针放在QObjectList之中。QObjectList是QList<QObject*>的别名,QList是Qt的一个列表容器。
    2、对象删除规则如下(注意:以下规则并非C++语法规则,而是Qt的对象树规则):
     基本规则:父对象会自动删除子对象。父对象拥有对象的所有权,在父对象被删除时会在析构函数中自动删除其子对象。
     手动删除子对象:当手动删除子对象时,会把该子对象从父对象的列表中移除,以避免父对象被删除时该子对象被再次删除。总之QObject对象不会被删除两次。
     当一个对象被删除时,会发送一个destroyed()信号,可以捕捉该信号以避免对QObject对象的悬垂引用。
    3、对象创建规则如下
     子对象通常应创建在堆中(使用new创建),当父对象被删除时,会自动删除该子对象,此时就不再需要使用delete将其删除了。
     对于Qt程序,父对象通常创建在栈上,不应创建在堆上(使用new创建)
     子对象不应创建在栈中,因为若父对象比子对象更早的结束生命期(即父对象创建于子对象之后),则子对象会被删除两次,第一次发生在父对象生命期结束时,由Qt对象树的规则,使用父对象删除子对象,第二次发生在子对象生命期结束时,由C++规则删除子对象。这种错误可使用先创建父对象后创建子对象的方法解决,依据C++规则,子对象会先被删除,由Qt对象树规则知,此时子对象会从父对象的列表中移除,当父对象结束生命期时,就不会再次删除子对象了。
    4、设置父对象的方法如下
     创建对象时,在构造函数中指定父对象。QObject类及其子类都有一个形如QObject *parent=0形参的构造函数,因此可以在创建对象时在构造函数中直接指定父对象。
     使用void QObject::setParent(QObject *parent)函数为该对象设置父对象。
    5、其他规则
    应确保每一个QObject对象在QApplication之后创建,在QApplication销毁之前销毁,因此QObject对象不能是static存储类型的,因为static对象将在main()返回之后才被销毁,其销毁时间太迟了。
    6、对象名称
    可以为每个对象设置一个对象名称,其主要作用是方便对对象树进行查询和管理。对象名称和对象是不同的,比如A ma; 其中ma是对象,若为ma设置一个名称为“SSS”,则对象ma的对象名称为“SSS”。
    对象名称由QObject的objectName属性指定(默认值为空字符串),该属性的读取和设置函数分别如下所示(注:对象的类名可通过QMetaObject::className()查询。)
    Qstring objectName() const //读取该对象名称
    void setObjectName(const QString &name); //设置该对象的名称为name

     示例2.15:Qt的对象树与对象的删除
    #include<QObject>
    #include <iostream>
    using namespace std;
    class A:public QObject{public:		//子类化QObject
        A(){}	        A(QObject *p):QObject(p){}
    ~A(){cout<<objectName().toStdString()<<"=~A"<<endl;} 	};
    class B:public QObject{public:		//子类化QObject
        B(){}			B(QObject *p):QObject(p){}
        ~B(){cout<<objectName().toStdString()<<"=~B"<<endl;}       };
    int main(int argc, char *argv[]){
    	A ma;         //父对象通常创建在栈上
    	A *pa1=new A(&ma);   //在堆上指定父对象
    	A *pa2=new A(&ma);		B *pb1=new B(pa1);		B *pb2=new B(pa1);
    	ma.setObjectName("ma");     //为各对象设置对象名
    	pa1->setObjectName("pa1");		pa2->setObjectName("pa2");
    	pb1->setObjectName("pb1");		pb2->setObjectName("pb2");
    	A ma1;		B mb1;
    	mb1.setParent(&ma1);  //在栈上把ma1指定为mb1的父对象,此处父对象创建于子对象之前。
    	ma1.setObjectName("ma1");		mb1.setObjectName("mb1");
    	B mb2;		A ma2;
    //mb2.setParent(&ma2);  /*错误,在栈上指定父对象时,父对象应创建于子对象之前,此处会导致子对象mb2被删除两次。*/
    return 0;	}
    

    运行结果如图2-5所示
    在这里插入图片描述
    7、查询对象树的信息,可使用以下QObject类中的成员函数
    在这里插入图片描述

    示例2.16:查询对象树
    #include<QDebug>   //需使用qDebug()函数,该函数的用法类似于C++的cout
    #include<QObject>
    class A:public QObject{ public:  A(){}  A(QObject *p):QObject(p){}  void f(){qDebug()<<"AF"; }};
    class B:public QObject{ public:B(){}  B(QObject *p):QObject(p){} void f(){qDebug()<<"BF";}};
    class C:public QObject{public:   int c;
        				C(){}    C(QObject *p):QObject(p){}   void f(){qDebug()<<"CF";}};
    int main(int argc, char *argv[]){
    A ma;       			 A *pa1=new A(&ma);			A *pa2=new A(&ma);
    B *pb1=new B(pa1);		B *pb2=new B(pa1);		C *pc1=new C(pb1);		C *pc2=new C(pb1);
    ma.setObjectName("ma");		pa1->setObjectName("pa1");	pa2->setObjectName("pa2");
    pb1->setObjectName("pb1");	pb2->setObjectName("pb2");	
    pc1->setObjectName("pc1");	pc2->setObjectName("pc2");
    pc2->c=2;					pc1->c=1;
    QObjectList st= ma.children();
    qDebug()<<"ma="<<ma.children();
    //以上输出:ma= (QObject(0x82d638, name = "pa1"), QObject(0x82d478, name = "pa2"))
    qDebug()<<"pb1="<<pb1->children();
    //以上输出:pb1= (QObject(0x840f38, name = "pc1"), QObject(0x840bf0, name = "pc2"))
    
    qDebug()<<"\n##### dumpObjectTree #####";
    ma.dumpObjectTree();           //输出见图2-6
    qDebug()<<"############";
    pb1->dumpObjectTree();         //输出见图2-6
    qDebug()<<"\n###### findChild #####";
    C* p1=ma.findChild<C*>("pc1");  //通过父对象ma获取指向子对象pc1的指针。
    p1->f();   //输出CF
    qDebug()<<p1->c;  //输出1
    C* p2=ma.findChild<C*>("pc2",Qt::FindDirectChildrenOnly); /*获取ma的直接子对象中名称为pc2的对象的指针,因为pc2不是ma的直接子类,所以该处返回NULL。*/
    //qDebug()<<p2->c;  //错误,此时p2指向的是NULL
    qDebug()<<"\n###### findChildren #####";
    QList<C*> s=ma.findChildren<C*>("pc1"); //Qt::FindDirectChildrenOnly
    qDebug()<<s;          //输出:(QObject(0x840f38, name = "pc1"))
    qDebug()<<s[0]->c;    //输出1
    QList<C*> s1=ma.findChildren<C*>("pc2",Qt::FindDirectChildrenOnly);
    qDebug()<<s1;   //输出一个空圆括号,因为pc2不是ma的直接子对象。
    //查找ma的直接子对象
    QList<C*> s2=ma.findChildren<C*>(QString(),Qt::FindDirectChildrenOnly);
    qDebug()<<s2;  //输出:(QObject(0x82d638, name = "pa1"), QObject(0x82d478, name = "pa2"))
    QList<C*> s3=ma.findChildren<C*>(); //获取ma的整个对象树结构的列表
    qDebug()<<s3;
    //以上输出:	 (QObject(0x82d638, name = "pa1"), 		QObject(0x840f00, name = "pb1"), 
    //QObject(0x840f38, name = "pc1"), 		QObject(0x840bf0, name = "pc2"), 
    //QObject(0x840b48, name = "pb2"), 		QObject(0x82d478, name = "pa2"))
    
        	return 0;			}
    

    运行结果及说明见图2-6
    在这里插入图片描述
    本文作者:黄邦勇帅(原名:黄勇)

    在这里插入图片描述

    展开全文
  • Qt 5——对象模型(对象树

    千次阅读 2021-02-18 21:23:17
    文章目录对象模型(对象树对象树与内存问题 对象模型(对象树) 在Qt中创建对象的时候会提供一个Parent对象指针,下面来解释这个parent到底是干什么的。 QObject是以对象树的形式组织起来的。 当你创建一个...

    对象模型(对象树)

    在这里插入图片描述

    在Qt中创建对象的时候会提供一个Parent对象指针,下面来解释这个parent到底是干什么的。

    • QObject是以对象树的形式组织起来的。
      • 当你创建一个QObject对象时,会看到QObject的构造函数接收一个QObject指针作为参数,这个参数就是 parent,也就是父对象指针。
        这相当于,在创建QObject对象时,可以提供一个其父对象,我们创建的这个QObject对象会自动添加到其父对象的children()列表。
      • 当父对象析构的时候,这个列表中的所有对象也会被析构。(注意,这里的父对象并不是继承意义上的父类!)
        这种机制在 GUI 程序设计中相当有用。例如,一个按钮有一个QShortcut(快捷键)对象作为其子对象。当我们删除按钮的时候,这个快捷键理应被删除。这是合理的。
    • QWidget是能够在屏幕上显示的一切组件的父类。
      • QWidget继承自QObject,因此也继承了这种对象树关系。一个孩子自动地成为父组件的一个子组件。因此,它会显示在父组件的坐标系统中,被父组件的边界剪裁。例如,当用户关闭一个对话框的时候,应用程序将其删除,那么,我们希望属于这个对话框的按钮、图标等应该一起被删除。事实就是如此,因为这些都是对话框的子组件。
      • 当然,我们也可以自己删除子对象,它们会自动从其父对象列表中删除。比如,当我们删除了一个工具栏时,其所在的主窗口会自动将该工具栏从其子对象列表中删除,并且自动调整屏幕显示。

    对象树与内存问题

    Qt 引入对象树的概念,在一定程度上解决了内存问题。

    • 当一个QObject对象在堆上创建的时候,Qt 会同时为其创建一个对象树。不过,对象树中对象的顺序是没有定义的。这意味着,销毁这些对象的顺序也是未定义的。
    • 任何对象树中的 QObject对象 delete 的时候,如果这个对象有 parent,则自动将其从 parent 的children()列表中删除;如果有孩子,则自动 delete 每一个孩子。Qt 保证没有QObject会被 delete 两次,这是由析构顺序决定的。

    如果QObject在栈上创建,Qt 保持同样的行为。正常情况下,这也不会发生什么问题。来看下下面的代码片段:

    {
        QWidget window;
        QPushButton quit("Quit", &window);
    }
    

    作为父组件的 window 和作为子组件的 quit 都是QObject的子类(事实上,它们都是QWidget的子类,而QWidget是QObject的子类)。这段代码是正确的,quit 的析构函数不会被调用两次,因为标准 C++要求,局部对象的析构顺序应该按照其创建顺序的相反过程。因此,这段代码在超出作用域时,会先调用 quit 的析构函数,将其从父对象 window 的子对象列表中删除,然后才会再调用 window 的析构函数。
    但是,如果我们使用下面的代码:

    {
        QPushButton quit("Quit");
        QWidget window;
        quit.setParent(&window);
    }
    

    情况又有所不同,析构顺序就有了问题。我们看到,在上面的代码中,作为父对象的 window 会首先被析构,因为它是最后一个创建的对象。在析构过程中,它会调用子对象列表中每一个对象的析构函数,也就是说, quit 此时就被析构了。然后,代码继续执行,在 window 析构之后,quit 也会被析构,因为 quit 也是一个局部变量,在超出作用域的时候当然也需要析构。但是,这时候已经是第二次调用 quit 的析构函数了,C++ 不允许调用两次析构函数,因此,程序崩溃了。

    由此我们看到,Qt 的对象树机制虽然帮助我们在一定程度上解决了内存问题,但是也引入了一些值得注意的事情。这些细节在今后的开发过程中很可能时不时跳出来烦扰一下,所以,我们最好从开始就养成良好习惯,在 Qt 中,尽量在构造的时候就指定 parent 对象,并且大胆在堆上创建。

    展开全文
  • python 对象树

    千次阅读 2017-01-22 01:29:29
    1、python 对象树 python的继承树中的每个节点即一个命名空间,最底下的叶子为对象实例,对象实例通过 __class__ 钩子与上边的类节点连接,类通过__base__钩子与更高的超类连接。位于最顶上的根节点是type元类。 ...

    1、python 对象树

    python的继承树中的每个节点即一个命名空间,最底下的叶子为对象实例,对象实例通过 __class__ 钩子与上边的类节点连接,类通过__base__钩子与更高的超类连接。位于最顶上的根节点是type元类。

    无论属性或者方法,最终都是在命名空间中爬树、进行查找,每个命名空间的存储以字典的形式进行,字典的键为属性或者方法的名称字符串

    每个对象的属性的查找的命名空间顺序 MRO 是固定的,之前可能是DFS 再 BFS,目前是C3算法来实现。C3算法保证了子类永远在父类前面。

    super 函数返回MRO顺序的下一个命名空间对象,主要用在多继承当中。

    对于经典的菱形继承。

    对于obj = D()  站在obj 对象的角度,obj 的mro顺序[D, B, C, A, object]. 

    2、supper 函数

    常见的 super 函数的两种用法:

    1、第二个参数是一个实例:

    super(B, obj) 返回下一个MRO搜索顺序的命名空间,此处的 mro 由obj.__class__ 提供。

    2、第二个参数是一个类:

    super(B, sometype) 返回下一个MRO搜索顺序的命名空间,此处的 mro 由 sometype 提供。

    python3中 super 函数可以直接不带参数。

    属性的搜索顺序:

    obj.attr

    obj.__getattribute__("attr")            #进行拦截。

    obj.__dict__["attr"] 及父类的.__dict__["attr"]             #如果在树中找到

    obj.__getattr__("attr")      #如果在树中没有定义,即没找到

     

    class D(object):
        def __init__(self):
            self.test=20
            self.test2=21
        def __getattribute__(self,name):
            if name=='test':
                return 0.
            else:
                return object.__getattribute__(self, name)
                #super(D, self).__getattribute__(name)
                #super().__getattribute__(name)   #python3中可以这样写

     

    注意这里超类使用 __getattribute__  通过 self 来正常的获取属性,即使属性不在超类中,解释器开洞!!!这样抛到高层的超类中,也是为了避免无限循环。

    3、静态方法、实例方法、类方法

    class Normal:  #通过实例对象来调用时有绑定
    	def func(x):
    		print("normal")
    n = Normal()
    n.func()        #实例对象来调用,实例对象自动绑定到方法的一个参数
    Normal.func(9)  #通过类调用,第一个参数可以任意传入
    
    class CM:  #通过实例对象和类来调用都需要绑定
            @classmethod
    	def func(x):
    		print("classMethod")
    cm = CM()
    cm.func()
    CM.func()   #实例对象和类自动绑定到第一个参数
    
    class StaticC:  #不绑定
    	@staticmethod
    	def func(x):
    		print("staticMethod")
    sobj = StaticC()
    sobj.func(3)
    StaticC.func(3)  #不绑定到参数

     

    python的所有方法都可以通过类实例对象和类来调用。对于普通方法而言,通过实例对象来调用,实例对象自动被绑定到第一个参数(约定名称为self),但是通过类来调用,不绑定该参数,所以该参数可以任意传入,没有任何约束。对于类方法,通过实例对象和类来调用,都自动绑定到第一个参数,所以类方法至少需要一个参数。对于静态方法,通过类和实例来调用,不做任何参数绑定,所以所有的参数都需要额外传入。

    4、dir() 函数属性获取

    __dict__ 只包含每个节点命名空间自身的属性,dir() 则会爬树,从实例爬到类,再爬完所有的父类,但是不包括类所属的类(即元类)。普通类都可以调用 .mro() 方法,但是该方法其实是终极元类 type 所有,包括 __base__,__class__ 都是从 type 元类所来,所以 dir() 列表中没有。

    5、type 与 object 的关系

    python 中一切都是对象,但是对象有其所属的类。所以存在两种关系:

    1、对象与类之间的某个对象是否属于某个类的 isinstance 的关系。

    2、类与类之间是否存在继承的 issubclass 的关系。

    object 是所有类的父类,所以 issubclass(anyclass, object) 都是 True。type 则是所有类的类,所有 isinstance(anyclass, type)都是 True.

     唯一的一个 False 是 issubclass(object, type).

    展开全文
  • java之TreeUtils生成一切对象树形结构

    千次阅读 2020-04-02 14:24:00
    比较常见的,如菜单,分类,部门等等,如果为每种类型都遍历递归生成形结构返回给前端,显得有些冗余且麻烦,并且其实逻辑都是一致的,只是遍历的对象不同而已,故其实可以通过面向接口思维,来实现这种通用...
  • Qt工作笔记-通过 对象树 或 delete this 释放对象

    千次阅读 多人点赞 2019-01-22 17:27:39
    目录     背景 实例 ...今天在写代码的时候,new出的界面,没有delete,因为是dialog,可以用exec() 把后面的界面阻塞掉,但个人并不喜欢这样,毕竟觉得强扭的瓜不甜!...1. 是使用Qt对象树...
  • Qt 之对象树与所有权

    千次阅读 2016-09-02 17:53:32
    简述QObjects在一个对象树中组织他们自己。当创建一个QObject时,如果使用了其他对象作为其父对象,那么,它就会被添加到父对象的children()列表中。这样一来,当父对象被销毁时,这个QObject也会被销毁。事实表明,...
  • C语言的引用计数与对象树

    千次阅读 2013-12-28 14:59:28
    引用计数与对象树我们经常在C语言中,用指针指向一个对象(Object)的结构,也称为句柄(Handle),利用不透明指针的技术把结构数据封装成对象,因此如果说在Java中,一切皆是对象的话,那么在C中,万物皆是指针,...
  • Qt对象树

    千次阅读 2016-11-05 15:23:04
    Qt中的对象树总的来说就是: 一个parent被析构时,会自动delete/析构它所有的children; 一个children被析构时,会自动从他的parent中移除; 举例:(在栈上)新建了一个QMainwindow后,再新建一个QDialog,...
  • Qt智能指针和QObject对象树系统(父子系统)结合使用出现的问题 Qt的智能指针是在Qt4.5的时候提出来的,目的是为了让Qt应用程序能够摆脱硬编码delete的问题,避免的内存泄漏。但是在我编写程序的时候,发现它和...
  • java中形菜单对象

    千次阅读 2020-11-05 11:13:55
    /** * 登陆成功之后系统主而需要的菜单的json对象构造器 * @param id * @param pid * @param title * @param icon * @param href * @param target * @param spread */ public TreeNode(Integer id, Integer pid, ...
  • Js 将数组按父子关系转换为对象树

    千次阅读 2018-09-06 16:17:19
    如果把最终的对象想象成一颗的话,这个方法就相当于从叶子节点开始到根节点逐渐建立一棵完整的。 与上一个方法相比,该方法时间复杂度较低,且代码量不大,实现代码如下: var nodes = [ {id: 10, ...
  • 对象数组转为结构

    千次阅读 2019-04-22 09:48:44
    在平时的业务中,相信我们一定都遇到过需要将后台传过来的一维数组转为结构。 var arr = [ { id: 1, pid: -1, }, { id: 2, pid: -1, }, { id: 3, pid: -1, }, { id: 4, pid: 1, }, { id: 5, pid: 2, }, { id...
  • 将JSON格式对象转为形结构对象

    千次阅读 2018-09-17 14:00:58
    changeTree (data) { if (data.length > 0) { data.forEach(item => { const parentId = item.parentId; if (parentId !== 0) { data.forEach(ele => { if (ele.id === parentId)...// 输出形结构数据  
  • qt学习笔记(六)之简析对象树

    千次阅读 2011-11-08 17:45:59
    Qt提供了一种机制,能够自动、有效的组织和管理继承自QObject的Qt对象,这种机制就是对象树。 Qt对象树在用户界面编程上是非常有用的。它能够帮助程序员减轻内存泄露的压力。 比如说当应用程序创建了一个具有父...
  • 提取是一个小JavaScript库,用于从任意对象图中提取对象树。 对象图通常具有循环并包含许多信息。 因此,线索是提取的对象树使用链接来打破对象引用循环,并且可以通过省略非请求信息而只是部分的。 树提取由自定义 ...
  • QT的对象树机制,parent指针。

    千次阅读 2015-12-16 15:02:01
    QObjects 是以对象树的形式组织起来的。当你创建一个 QObject 对象时,会看到 QObject 的构造函数接收一个 QObject 指针作为参数,这个参数就是 parent,也就是父对象指针。这相当于,在创建 QObject 对象时,可以...
  • Java代码实现封装多级结构对象

    千次阅读 2019-02-27 09:47:34
    在开发中,我们经常见到,前端展示树状结构的,这时候就需要后端去封装一个多级结构对象,前端根据这样结构的数据去渲染数据,这篇文章讲的是如何封装成多级结构对象。 正文: 1.先封装个结构的对象 @Data...
  • Java对象转换,代码比较简单 提供了两个查询方法: No.1 :Map<String,List<Tree>>arrMap= queryGroupToMap();//No.1(不推荐使用,运行时间较长) No.1.1:Map<String,List<Tree>>arrMap=.....
  • 最近想把家里的网站升级一下,这个防火布网站主要是把后台重新做一下,昨天写到了数据的显示,今天就写保存了。后台是用vue element写的,感觉这个框架非常好用,有兴趣的可以了解一下...分类对象:Category packa...
  • JS对象递归遍历

    千次阅读 2018-11-25 16:46:16
    总所周知,遍历对象的属性用for(x in object)。但是,如果遍历的对象结构是二叉树,这种方法显然不够用了,所以下需要用递归遍历对象查找数据的方法: var tree = { "id": 0, "name": &...
  • 以前都是SQLyog,转到Navicat之后发现没有创建表的sql语句,有网友说右键有个对象信息,然后就能看到,那是以前的版本,现在的版本不一样了,今天记录一下: 点开后有个DDL可以看到sql语句了,不得不说,新版本这个...
  • js-将数组对象结构转为形结构

    千次阅读 2019-02-14 16:19:48
    会遇到这样的需求,将后端返回的列表集合按照父子关系转为形结构;进行展示 这个时候就需要我们使用js将从后端获取到的数据进行转化了。 假设后端获取到的数据为: var treeList = [ { title: '系统管理', ...
  • 当Dom构建完成时,...Firefox将渲染中的元素称为frames,WebKit则用renderer或渲染对象来描述这些元素。 一个渲染对象知道怎么布局及绘制自己及它的children。 RenderObject是Webkit的渲染对象基类,它的定义如下
  • 决策与随机森林初探

    万次阅读 2018-08-19 13:11:04
    决策的最关键的问题,如何选择划分属性的顺序才能使得决策的平均性能最好 举例: 这堆西瓜的熵是Ent(D),按照某种属性划分之后这堆西瓜的熵是Ent(D′),Ent(D′) &amp;amp;amp;amp;amp;amp;amp;amp;...
  • 什么是DOM?你了解DOM吗?

    万次阅读 多人点赞 2020-06-04 14:17:26
    在找工作的过程中,看到了招聘信息上有写这么一条信息: ... 看见这条信息我心里安心了不少,都是一些基础...DOM提供了对整个文档的访问模型,将文档作为一个形结构,的每个结点表示了一个HTML标签或标签内的文本项。
  • Java将List对象列表转为形结构

    千次阅读 2020-03-01 19:21:53
    经常遇到 菜单、部门等对象列表,输出到前端的时候需要转换成树状结构,一般人都想到递归调用,个人不是很喜欢递归,重写一个简单易懂方法针对这类小需求。 假设查询部门对象列表,部门对象为 SysOrg 如下: @Table...
  • 树状结构对象的获取及遍历

    千次阅读 2017-01-06 14:29:07
    以下是我做的地区二级联动菜单,我是在页面初始化时从数据库中获取已经封装好的Area对象,这个对象包含我们需要的数据结构,只需在页面上序列化成Json然后遍历Json就Ok了。 Area对象 package ...
  • 小记:对象结构数据转结构 /** * 对象转为结构 * @param list 结构列表 */ setTreeData(list: any) { let temp: any = {}; let tree = []; // 以对象id为序号储存到临时列表 for (let j in list) { if (j)...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 549,973
精华内容 219,989
关键字:

对象树