QT与JAVA的比较一

11 篇文章 0 订阅
4 篇文章 0 订阅
Qt 相当于 Java 中的 (SWT 或者 SWING) + Collections + Thread + Java3D + Network + ...

唯一可以挂上关系的 就是 他们都跨平台

Java 的运行是建立在虚拟机上的,在虚拟机上 一次编译 到处运行。
但虚拟机是平台各异的,执行代码格式统一。
Qt 程序的运行是建立在 Qt Framework上的。一次编码,到处编译。

但Framework是平台各异的,编程接口统一。

一.点击按钮

JAVA SWING 例子

(1)匿名类

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class AnonymousEvent extends JFrame{
 JButton btn;
 public AnonymousEvent(){
  super("Java事件监听机制");
  setLayout(new FlowLayout());
  setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  btn=new JButton("点击");
  
  btn.addActionListener(
   new ActionListener(){
    public void actionPerformed(ActionEvent e){
     Container c=getContentPane();
     c.setBackground(Color.red);
    }
   }
  );
  
  getContentPane().add(btn);
  setBounds(200,200,300,160);
  setVisible(true);
 }
 public static void main(String args[]){
  new AnonymousEvent();
 }
}


(2)内部类

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class InnerClassEvent extends JFrame{
 JButton btn;
 public InnerClassEvent(){
  super("Java事件监听机制");
  setLayout(new FlowLayout());
  setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  btn=new JButton("点击");
  btn.addActionListener(new InnerClass());
  getContentPane().add(btn);
  setBounds(200,200,300,160);
  setVisible(true);
 }
 
 class InnerClass implements ActionListener{
  public void actionPerformed (ActionEvent e){
   Container c=getContentPane();
   c.setBackground(Color.red);
  }
 }
 
 public static void main(String args[]){
  new InnerClassEvent();
 }
}


(3)外部类

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class OuterClassEvent extends JFrame{
 JButton btn;
 public OuterClassEvent(){
  super("Java事件监听机制");
  setLayout(new FlowLayout());
  setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  btn=new JButton("点击");
  btn.addActionListener(new OuterClass(this)); //外部类作监听器和内部类作监听器的区别是:用外部类的时候要把this传出去,就像本例
  getContentPane().add(btn);
  setBounds(200,200,300,160);
  setVisible(true);
 }
 public static void main(String args[]){
  new OuterClassEvent();
 }
}

class OuterClass implements ActionListener{
 OuterClassEvent oce;
 public OuterClass(OuterClassEvent oce){
  this.oce = oce;
 }
 public void actionPerformed(ActionEvent e){
  Container c=oce.getContentPane();
  c.setBackground(Color.red);
 }
}

(4)使用类本身作为事件监视器

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

class ThisClassEvent extends JFrame implements ActionListener{
 JButton btn;
 public ThisClassEvent(){
  super("Java事件监听机制");
  setLayout(new FlowLayout());
  setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  btn=new JButton("点击");
  btn.addActionListener(this); //自身类作为事件的监听器:只需要传this就可以了
  getContentPane().add(btn);
  setBounds(200,200,300,160);
  setVisible(true);
 }
 
 public void actionPerformed (ActionEvent e){
  Container c=getContentPane();
  c.setBackground(Color.red);
 }
 
 public static void main(String args[]){
  new ThisClassEvent();
 }
}




QT (使用信号槽)

#include <QtGui/QApplication> 
#include <QtGui/QPushButton> 
 
int main(int argc, char *argv[]) 
{ 
        QApplication a(argc, argv); 
        QPushButton *button = new QPushButton("Quit"); 
        QObject::connect(button, SIGNAL(clicked()), &a, SLOT(quit())); 
        button->show(); 
        return a.exec(); 
}


我是华丽的分割线///

二.基本窗体和菜单添加

JFRAME的继承结构:
java.lang.Object
  java.awt.Component
      java.awt.Container
          java.awt.Window
              java.awt.Frame
                  javax.swing.JFrame


Java GUI设计中有一个比较有意思的程序实现就是菜单的添加,以下是本人测试通过的最基本的为JFrame添加多级菜单项的代码,希望对JFrame的学习有帮助。
可运行的代码如下:

import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuItem;
import javax.swing.JFrame;

public class MainFrame extends JFrame{
MenuBar mb = new MenuBar();//创建菜单组对象
Menu m = new Menu("文件");//第一级菜单
MenuItem open = new MenuItem("打开");//第二级菜单
MenuItem close = new MenuItem("关闭");
MenuItem exit = new MenuItem("推出");

MainFrame(){
super("添加菜单测试");
setSize(500,500);//设置窗口大小
m.add(open);//将二级菜单添加到一级菜单上
m.add(close);
m.add(exit);
mb.add(m);//把一级菜单加到菜单组对象里面
setMenuBar(mb);//将菜单组对象加到JFrame中
setVisible(true);
}
public static void main(String args[]){
MainFrame mf = new MainFrame();
}
}

QT菜单实现

#include "MainWindow.h"
#include <QtGui/QWidget>
#include <QtGui/QMenuBar>
#include <QtGui/QMenu>
#include <QtGui/QAction>
#include <QtGui/QApplication>
#include <QtGui/QToolBar>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent) {
    createActions();
    createMenus();
    createToolBars();
}

MainWindow::~MainWindow() {
}

void MainWindow::createActions() {
    // QAction: 一建立, 二设置属性, 如图标, 快捷键, 事件处理.
    newAction = new QAction(tr("&New"), this);
    newAction->setIcon(QIcon("./images/Adium.png"));
    newAction->setShortcut(tr("Ctrl+N"));
    newAction->setStatusTip(QString("Create a new file."));
    QObject::connect(newAction, SIGNAL(triggered()), qApp, SLOT(aboutQt()));

    openAction = new QAction(tr("&Open"), this);
    saveAction = new QAction(tr("&Save"), this);
    saveAsAction = new QAction(tr("Save as"), this);
}

void MainWindow::createMenus() {
    // menuBar()第一次被调用时QMainWindow会生一个一QMenuBar在窗体上,
    // 且返回一个QMenu的指针.
    // menuBar()->addMenu()会生成一个QMenu且返回他的指针.
    // QMenu加入一个QAction, 就可以对事件进行反应了
    // 一个QAction可以被多个地方使用, 与Java的Action一样.
    fileMenu = menuBar()->addMenu(tr("&File"));
    fileMenu->addAction(newAction);
    fileMenu->addAction(openAction);
    fileMenu->addSeparator();
    fileMenu->addAction(saveAction);
    fileMenu->addAction(saveAsAction);

    editMenu = menuBar()->addMenu(tr("&Edit"));
    editMenu->addAction(newAction);
    editMenu->addAction(openAction);
    editMenu->addAction(saveAction);
    editMenu->addAction(saveAsAction);
}

void MainWindow::createToolBars() {
    // 跟创建菜单一个相似
    toolBar = addToolBar(tr("&File"));
    toolBar->addAction(newAction);
    toolBar->addAction(openAction);
    toolBar->addSeparator();
    toolBar->addAction(saveAction);
    toolBar->addAction(saveAsAction);
}



///我是华丽的分割线///

三.容器类对比

线性表,链表,哈希表是常用的数据结构,在进行Java研发时,JDK已为我们提供了一系列相应的类来实现基本的数据结构。这些类均在java.util包中。本文试图通过简单的描述,向读者阐述各个类的作用连同怎样正确使用这些类。

Collection
List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
Set
Map

├Hashtable
├HashMap
└WeakHashMap

Collection接口
  Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。
  任何实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数用于创建一个空的Collection,有一个Collection参数的构造函数用于创建一个新的Collection,这个新的Collection和传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection。
  怎样遍历Collection中的每一个元素?不论Collection的实际类型怎样,他都支持一个iterator()的方法,该方法返回一个迭代子,使用该迭代子即可逐一访问Collection中每一个元素。典型的用法如下:
    Iterator it = collection.iterator(); // 获得一个迭代子
    while(it.hasNext()) {
      Object obj = it.next(); // 得到下一个元素
    }

  由Collection接口派生的两个接口是List和Set。

List接口
  List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。
和下面要提到的Set不同,List允许有相同的元素。
  除了具备Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。
  实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。

LinkedList类
  LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法在LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。
  注意LinkedList没有同步方法。假如多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:
    List list = Collections.synchronizedList(new LinkedList(...));

ArrayList类
  ArrayList实现了可变大小的数组。他允许任何元素,包括null。ArrayList没有同步。
size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。
  每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前能够调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。
  和LinkedList相同,ArrayList也是非同步的(unsynchronized)。

Vector类
  Vector很类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和ArrayList创建的Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationException,因此必须捕获该异常。

Stack 类
  Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop方法,更有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。

Set接口
  Set是一种不包含重复的元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。
  很明显,Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。
  请注意:必须小心操作可变对象(Mutable Object)。假如一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。

Map接口
  请注意,Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个value。Map接口提供3种集合的视图,Map的内容能够被当作一组key集合,一组value集合,或一组key-value映射。

Hashtable类
  Hashtable继承Map接口,实现一个key-value映射的哈希表。任何非空(non-null)的对象都可作为key或value。
  添加数据使用put(key, value),取出数据使用get(key),这两个基本操作的时间开销为常数。
Hashtable通过initial capacity和load factor两个参数调整性能。通常缺省的load factor 0.75较好地实现了时间和空间的均衡。增大load factor能够节省空间但相应的查找时间将增大,这会影响像get和put这样的操作。
使用Hashtable的简单示例如下,将1,2,3放到Hashtable中,他们的key分别是”one”,”two”,”three”:
    Hashtable numbers = new Hashtable();
    numbers.put(“one”, new Integer(1));
    numbers.put(“two”, new Integer(2));
    numbers.put(“three”, new Integer(3));

  要取出一个数,比如2,用相应的key:
    Integer n = (Integer)numbers.get(“two”);
    System.out.println(“two = ” + n);

  由于作为key的对象将通过计算其散列函数来确定和之对应的value的位置,因此任何作为key的对象都必须实现hashCode和equals方法。hashCode和equals方法继承自根类Object,假如您用自定义的类当作key的话,要相当小心,按照散列函数的定义,假如两个对象相同,即obj1.equals(obj2)=true,则他们的hashCode必须相同,但假如两个对象不同,则他们的hashCode不一定不同,假如两个不同对象的hashCode相同,这种现象称为冲突,冲突会导致操作哈希表的时间开销增大,所以尽量定义好的hashCode()方法,能加快哈希表的操作。
  假如相同的对象有不同的hashCode,对哈希表的操作会出现意想不到的结果(期待的get方法返回null),要避免这种问题,只需要牢记一条:要同时复写equals方法和hashCode方法,而不要只写其中一个。
  Hashtable是同步的。

HashMap类
  HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null value和null key。,但是将HashMap视为Collection时(values()方法可返回Collection),其迭代子操作时间开销和HashMap的容量成比例。因此,假如迭代操作的性能相当重要的话,不要将HashMap的初始化容量设得过高,或load factor过低。

WeakHashMap类
  WeakHashMap是一种改进的HashMap,他对key实行“弱引用”,假如一个key不再被外部所引用,那么该key能够被GC回收。

总结
  假如涉及到堆栈,队列等操作,应该考虑用List,对于需要快速插入,删除元素,应该使用LinkedList,假如需要快速随机访问元素,应该使用ArrayList。
  假如程式在单线程环境中,或访问仅仅在一个线程中进行,考虑非同步的类,其效率较高,假如多个线程可能同时操作一个类,应该使用同步的类。
  要特别注意对哈希表的操作,作为key的对象要正确复写equals和hashCode方法。
  尽量返回接口而非实际的类型,如返回List而非ArrayList,这样假如以后需要将ArrayList换成LinkedList时,客户端代码不用改变。这就是针对抽象编程。

同步性
Vector是同步的。这个类中的一些方法确保了Vector中的对象是线程安全的。而ArrayList则是异步的,因此ArrayList中的对象并不是线程安全的。因为同步的需要会影响执行的效率,所以假如您无需线程安全的集合那么使用ArrayList是个很好的选择,这样能够避免由于同步带来的不必要的性能开销。
数据增长
从内部实现机制来讲ArrayList和Vector都是使用数组(Array)来控制集合中的对象。当您向这两种类型中增加元素的时候,假如元素的数目超出了内部数组现在的长度他们都需要扩展内部数组的长度,Vector缺省情况下自动增长原来一倍的数组长度,ArrayList是原来的50%,所以最后您获得的这个集合所占的空间总是比您实际需要的要大。所以假如您要在集合中保存大量的数据那么使用Vector有一些优势,因为您能够通过配置集合的初始化大小来避免不必要的资源开销。
使用模式
在ArrayList和Vector中,从一个指定的位置(通过索引)查找数据或是在集合的末尾增加、移除一个元素所花费的时间是相同的,这个时间我们用O(1)表示。但是,假如在集合的其他位置增加或移除元素那么花费的时间会呈线形增长:O(n-i),其中n代表集合中元素的个数,i代表元素增加或移除元素的索引位置。为什么会这样呢?以为在进行上述操作的时候集合中第i和第i个元素之后的任何元素都要执行位移的操作。这一切意味着什么呢?
这意味着,您只是查找特定位置的元素或只在集合的末端增加、移除元素,那么使用Vector或ArrayList都能够。假如是其他操作,您最好选择其他的集合操作类。比如,LinkList集合类在增加或移除集合中任何位置的元素所花费的时间都是相同的?O(1),但他在索引一个元素的使用缺比较慢-O(i),其中i是索引的位置.使用ArrayList也很容易,因为您能够简单的使用索引来代替创建iterator对象的操作。LinkList也会为每个插入的元素创建对象,任何您要明白他也会带来额外的开销。
最后,在《Practical Java》一书中Peter Haggar建议使用一个简单的数组(Array)来代替Vector或ArrayList。尤其是对于执行效率需要高的程式更应如此。因为使用数组(Array)避免了同步、额外的方法调用和不必要的重新分配空间的操作。

 VectorHashtable都是jdk1.0的就有了的。后来到java2后,java的容器框架改很多,了兼容,就VectorHashtable别实现了新的容器框架的ListMapArrayListHashMap都是java2(也就是jdk1.2)后才有的

1. 安全、效率方面:假如要实现安全,要用VectorHashtable,否则则ArrayListHashMap,因ArrayListHashMap不考安全的问题,所以效率要高些。但Collections能够解决问题

           Collections.synchronizedList
Collections.synchronizedMap
2. 源方面:当两者的容量已满时,他都会自其容量,但Vector是按其容量的一倍增,而ArrayList按其容量的50%增加,所以Vector更能源。
3.迭代器:VectorHashtable使用Enumeration,ArrayListHashMap使用Iterator
      ARRAY是必在声明的; ARRYLIST也是和Vector,能够自增加
ListSet的区
     List用来处理序列,而Set用来处理集。
List中的内容能够重复,而Set则不行。
Vector,ArrayList和Hashtable,HashMap的区分:
        1.Vector和ArrayList是数值联系对象。按照插入的顺序进行排列,能够有重复值。
        2.Hashtable和HashMap是对象联系对象。按照自己的排列方式进行排序,不能够有重复值
HashMap:继承了Map接口,实现用Keys来存储和访问Values,Keys和Values都能够为空,他和Hashtable类的区别在于Hashtable类的Keys不能为null,
Vector内部实际是以Array实现的,也通过元素的整数索引来访问元素,但他只能存放java.lang.Object对象,不能用于存放基本类型数据,比如要存放一个整数10,得用new Integer(10)构造出一个Integer包装类对象再放进去。 
更有一点:
HASHMAP不是同步的,线程不安全的,HASHTABLE是同步的,线程安全的?  
在Hashtable中,任何涉及到更新其中存放的内容的方法都是同步的   
  如:   
  public   synchronized   Object   put(Object   key,   Object   value)   {}   
  public   synchronized   Object   get(Object   key)   {}   
  ...   
    
  以此来确保Hashtable不会被多个线程同时更改
因为HashMap的公共方法上没加synchronized关键字
怎样理解synchronized  关键字呢?
比如:
用synchronized和不用synchronized简单的说就是   
  1个房子有2个门,用synchronized的是从有锁的门   
  进入房子,并且进入后锁门,出来后门打开。   
  而不用synchronized的是从没有锁的门进入。   
  从有锁的门进入的人只管把这个门锁上,但没办法干预从无锁的门进入的人   
  所以假如clear()方法是synchronized,他进门后取走任何鸡蛋   
  这时假如一个非synchronized的get()方法想进门取得一个鸡蛋就出错了


QT的容器类

QT不仅支持C++的STL模板库,同时自己也定义了一套容器类和与之操作的算法类,使用QT定义的这一套库,可以使在各个平台的表现相同。QT的这些容器被设计为更轻便,更安全和更容易使用。容器类是隐含共享(implicitly)的,可重入(reentrant)的和线程安全的。为容器遍历提供了两种迭代器(java-style,STL-style),并且QT提供了foreach关键字,更加方便元素遍历。


连续容器:

QVector<T>  

它是QT里最常见的容器类,它对应STL中的Vector<T>容器,是动态数组,提供快速的索引存取。

QList<T>  QLinkList<T>  QVarLengthArray<T>提供类似的功能。

可以参考帮助文档,查看QList的具体使用方法,以下列出部分使用示例:

1.创建各种类型的vector:

  QVector<int> integerVector;

  QVector<QString> stringVector;

2.创建一定数目项的vector

  QVector<QString> vector(200);

3.带初始化的创建vector

  QVector<QString> vector(200, "Pass");

也可以使用fill赋值:

  QVector<QString> vector(3);

  vector.fill("Yes");

  // vector: ["Yes", "Yes", "Yes"]

  vector.fill("oh", 5);

  // vector: ["oh", "oh", "oh", "oh", "oh"]

4.QVector像c++的Vector一样提供[]下标操作,并从0项开始。还提供at()的只读操作,at()比[]更快,因为它不会导致深度复制。

  if (vector[0] == "Liz")

  vector[0] = "Elizabeth";

  for (int i = 0; i < vector.size(); ++i) {

    if (vector.at(i) == "Alfonso")

        cout << "Found Alfonso at position " << i << endl;

  }

5.你可以使用indexOf,lastIndexOf来查询获取某项值的索引:

  QVector<QString> vector;

  vector << "A" << "B" << "C" << "B" << "A";

  vector.indexOf("B");            // returns 1

  vector.indexOf("B", 1);         // returns 1

  vector.indexOf("B", 2);         // returns 3

  vector.indexOf("X");            // returns -1

  vector.lastIndexOf("B");        // returns 3

  vector.lastIndexOf("B", 3);     // returns 3

  vector.lastIndexOf("B", 2);     // returns 1

  vector.lastIndexOf("X");        // returns -1

也可以用contains()查看是否包含某元素,返回bool值。

6.通过append,operator<<,prepend,insert添加元素。(对于较大的vector来说,在开头和中间插入项都是相当耗时的。这种情况更适合使用QLinkedList<T>)

  QVector<QString> vector(0);

  vector.append("one");

  vector.append("two");

  vector.append("three");

  // vector: ["one", "two", "three"]

  QVector<QString> vector(0);

  vector << “one” << “two” << “three”;

  // vector: ["one", "two", "three"]

  QVector<QString> vector;

  vector.prepend("one");

  vector.prepend("two");

  vector.prepend("three");

  // vector: ["three", "two", "one"]

  QVector<QString> vector;

  vector << "alpha" << "beta" << "delta";

  vector.insert(2, "gamma");

  // vector: ["alpha", "beta", "gamma", "delta"]

7.size() resize() isEmpty() capacity()等和容器大小相关操作。

8.相关转化:toList()  toStdVector()  

  QVector<double> vect;

  vect << "red" << "green" << "blue" << "black"; 

  QList<double> list = vect.toList();

  // list: ["red", "green", "blue", "black"]

  QVector<double> vector;

  vector << 1.2 << 0.5 << 3.14;

  std::vector<double> stdvector = vector.toStdVector();

(以下容器操作函数的使用将不再累述,和查阅帮助文档,并且和QVector的使用方法是一样的。)

QLinkedList<T>

前面提到,它适合随机插入项,其原因是它的链式结构。他提供了常量时间的插入删除,却不能提供快速的随机存取操作。不提供[]操作,它的遍历元素是通过迭代器完成的。

QList<T>

它是个数组列表,结合了上面两种结构的优点,它支持随机存取,在它的任意一端插入和删除都是非常快速的并且对于千项以上的列表,在中间插入和删除也是很快的。学过数据结构的都会清楚这三者的结构区别。如果非要每个项元素都相邻那就只能用QVector。

QString<t>

它是QList<QString>的子类,它为字符串操作提供了更通用的操作。

QStack<T> QQueue<T>

他们是栈和队列结构的实现,QStack提供pop() push() swap() top()操作,它继承自QVector<T>

QQueue<T>提供dequeue() enqueue() head() swap操作。继承自QList<T>。


关联容器

QSet<T>

它提供一个键值对集合,可以快速的进行查找,

QMap<Key, T> QMultiMap<Key, T>

QMap是一个以升序键顺序存储键值对的数据结构,QMultiMap是QMap基础上提供可以存储多值的maps,这样就是说一个键对应多个值了。

下面是创建一个QString-int的maps

  QMap<QString, int> map;

可以这样插入值

  map["one"] = 1;

  map["three"] = 3;

  map["seven"] = 7;

也可以这样:

  map.insert("twelve", 12);

查询一个值使用[] 或者value(“**”)

  int num1 = map["thirteen"];

  int num2 = map.value("thirteen");

查询是否存在一个值:

  if (map.contains("TIMEOUT"))

      timeout = map.value("TIMEOUT");

一般推荐使用contains() value()而不是[]。

QHash<Key, T> QMultiHash<Key, T>

QHash<Key, T>是个在哈希表中存储键值对的结构。它的接口几乎和QMap相同,但它提供了更快的查找功能。

QHash为它的内部哈希表自动分配最初的存储区域,并在有项被插入或者删除时重新划分所分配的区域大小。也可以调用reserve()或者squeeze()来指定或者压缩希望存储到哈希表的项的数目,以进行性能调整。通常的做法是利用我们预期的最大的项的数目来调用reserve(),然后插入数据,最后如果有多出的项,则调用squeeze()以使内存减到最小。

迭代器

对于每种容器都有两种风格的迭代器——java风格和STL风格。Java风格的更易于使用而以很少量性能作为了代价,而STL风格的可以结合STL的算法从而更加强大。

这里我们主讲QList和QMap的迭代器为例。


Java-Style:

Java风格的迭代器分为两种:只读迭代器,读写迭代器。只读迭代器就是Q*Iterator<T> (例如QVectorIterator<T>),而读写迭代器则像QMutable*Iterator<T>这种(例如:QMutableVectorIterator<T>)。

Containers

Read-only iterator

Read-write iterator

QList<T>, QQueue<T>

QListIterator<T>

QMutableListIterator<T>

QLinkedList<T>

QLinkedListIterator<T>

QMutableLinkedListIterator<T>

QVector<T>, QStack<T>

QVectorIterator<T>

QMutableVectorIterator<T>

QSet<T>

QSetIterator<T>

QMutableSetIterator<T>

QMap<Key, T>, QMultiMap<Key, T>

QMapIterator<Key, T>

QMutableMapIterator<Key, T>

QHash<Key, T>, QMultiHash<Key, T>

QHashIterator<Key, T>

QMutableHashIterator<Key, T>

Java风格迭代器的有效位置:


下面是一个典型的使用例子:  

  QList<QString> list;

  list << "A" << "B" << "C" << "D";

  QListIterator<QString> i(list);

  while (i.hasNext())

      qDebug() << i.next();

下面展示如何向后遍历

  QListIterator<QString> i(list);

  i.toBack();

  while (i.hasPrevious())

      qDebug() << i.previous();

如果左边有项那么hasPrevious()将返回true。previous()返回迭代器左边的项并且往前移一个位置。可以看如图:


下表是QListIterator 的API及说明

Function

Behavior

toFront()

迭代器移到最前,第一项的前

toBack()

迭代器移到最后,最后一项的后面

hasNext()

如果不是list的最后,就返回true

next()

返回下一项,并迭代器向后移一位

peekNext()

返回下一项,迭代器并不移动

hasPrevious()

如果不是list的最前,就返回true

previous()

返回前一项,并迭代器向后移一位

peekPrevious()

返回前一项,迭代器并不移动

下面是Mutable iterator读写迭代器使用说明:

QList<int>移除基数项:

  QMutableListIterator<int> i(list);

  while (i.hasNext()) {

      if (i.next() % 2 != 0)

          i.remove();

  }

下面是QMap的迭代器示例,用法和前面是类似的:

  QMap<QString, QString> map;

  map.insert("Paris", "France");

  map.insert("Guatemala City", "Guatemala");

  map.insert("Mexico City", "Mexico");

  map.insert("Moscow", "Russia");

  ...

  QMutableMapIterator<QString, QString> i(map);

  while (i.hasNext()) {

      if (i.next().key().endsWith("City"))

          i.remove();

  }

  QMap<int, QWidget *> map;

  QHash<int, QWidget *> hash;

  QMapIterator<int, QWidget *> i(map);

  while (i.hasNext()) {

      i.next();

      hash.insert(i.key(), i.value());

  }


STL-Style:

STL风格是迭代器不仅支持Qt的通用算法,还兼容STL的。

和java风格的类似,它也有两种风格的迭代器,只读的(const_iterator)和读写的(iterator)。

Containers

Read-only iterator

Read-write iterator

QList<T>, QQueue<T>

QList<T>::const_iterator

QList<T>::iterator

QLinkedList<T>

QLinkedList<T>::const_iterator

QLinkedList<T>::iterator

QVector<T>, QStack<T>

QVector<T>::const_iterator

QVector<T>::iterator

QSet<T>

QSet<T>::const_iterator

QSet<T>::iterator

QMap<Key, T>, QMultiMap<Key, T>

QMap<Key, T>::const_iterator

QMap<Key, T>::iterator

QHash<Key, T>, QMultiHash<Key, T>

QHash<Key, T>::const_iterator

QHash<Key, T>::iterator

用过c++ STL库的就对此很容易上手。下面是QListIterator的例子:

  QList<QString> list;

  list << "A" << "B" << "C" << "D";

  QList<QString>::iterator i;

  for (i = list.begin(); i != list.end(); ++i)

      *i = (*i).toLower();

STL风格的迭代器可允许的位置与java风格的有所不同


遍历需要我们自己增加缩减迭代器,例如:

  QList<QString>::const_iterator i;

  for (i = list.constBegin(); i != list.constEnd(); ++i)

      qDebug() << *i;

QList<QString> list;

  list << "A" << "B" << "C" << "D";

  QList<QString>::iterator i = list.end();

  while (i != list.begin()) {

      --i;

      *i = (*i).toLower();

  }

下面是QMap的例子:

  QMap<int, int> map;

  ...

  QMap<int, int>::const_iterator i;

  for (i = map.constBegin(); i != map.constEnd(); ++i)

      qDebug() << i.key() << ":" << i.value();


Foreach关键字

下面这个用foreach去遍历QLinkedList<QString>

  QLinkedList<QString> list;

  ...

  QString str;

  foreach (str, list)

      qDebug() << str;

可以再循环里使用break

  QLinkedList<QString> list;

  ...

  foreach (const QString &str, list) {

      if (str.isEmpty())

          break;

      qDebug() << str;

  }

QMap 和 QHash 中,如果你想遍历键和值,你可以用iterators(更快),或者这样写:

  QMap<QString, int> map;

  ...

  foreach (const QString &str, map.keys())

      qDebug() << str << ":" << map.value(str);

或者:

  QMultiMap<QString, int> map;

  ...

  foreach (const QString &str, map.uniqueKeys()) {

      foreach (int i, map.values(str))

          qDebug() << str << ":" << i;

  }


类容器类(Container-Like Class)

QVarLengthArray<T>

C++不支持在栈内存中提供可变长度的数组,例如下面:

int myfunc(int n)

{

    int table[n + 1];  // WRONG

    ...

    return table[n];

}

只能在堆内存中实现:

int myfunc(int n)

{

    int *table = new int[n + 1];

    ...

    int ret = table[n];

    delete[] table;

    return ret;

}

但是如果myfunc在应用程序内循环中调用非常频繁,那么堆内存分配将会变得缓慢,这种情况,QT为我们提供了QVarLengthArray来解决。

int myfunc(int n)

{

    QVarLengthArray<int, 1024> array(n + 1);

    ...

    return array[n];

}

值得注意的是,1.它的API是低水平的(low-level)的,他没有提供迭代器,和QVector的功能函数。2.如果值是基本类型,它将不会初始化内存。3.QVector使用隐含共享作为内存的优化,QVarLengthArray并没有提供,然而,它因为减少了经常性消费而显得性能稍微好些,特别是在紧凑的循环里。总的来说,它是为了方便用户使用在很少部分情况。

QCache<Key, T>

提供一个cache去存储Key-T键值对的对象。例如:

  QCache<int, Employee> cache;

插入对象到cache

  Employee *employee = new Employee;

  employee->setId(37);

  employee->setName("Richard Schmit");

  ...

  cache.insert(employee->id(), employee);

QCache的好处是自动获取的对象的拥有权(ownership)。你可以指定插入对象的花费,totalCost() maxCost()。maxCost()默认是100。

  QCache<int, MyDataStructure> cache(5000);

QContiguousCache<T>

QContiguousCache是一个提供连续Cache存储器的模板类。和QCache不同的是,它要求一个约束——相邻(Contiguous)。这有利于用户交互界面最普遍的数据需求。这样的约束使它比QCache消耗更少的内存和处理器周期。

简单的使用QContiguousCache的方式是使用append() prepend()

  MyRecord record(int row) const

  {

      Q_ASSERT(row >= 0 && row < count());

  

      while(row > cache.lastIndex())

          cache.append(slowFetchRecord(cache.lastIndex()+1));

      while(row < cache.firstIndex())

          cache.prepend(slowFetchRecord(cache.firstIndex()-1));

  

      return cache.at(row);

  }

可以查看文档中Contiguous Cache Example的例子。

QPair<T1, T2>

这个在STL中也是有的(pair)用来储存键值对。它用得更多的是做为函数的返回值。

看下面这个例子:存储一个QString键double值的QPair

  QPair<QString, double> pair;

使用first second来修改值

  pair.first = "pi";

  pair.second = 3.14159265358979323846;


算法复杂性比较

Constant time: O(1). 常数时间复杂度

Logarithmic time: O(log n). 对数时间复杂度

Linear time: O(n). 线性时间复杂度

Linear-logarithmic time: O(n log n). 线性对数时间复杂度

Quadratic time: O(n²). 平方时间复杂度

 顺序容器类操作时间复杂度比较:

Index lookup

Insertion

Prepending

Appending

QLinkedList<T>

O(n)

O(1)

O(1)

O(1)

QList<T>

O(1)

O(n)

Amort. O(1)

Amort. O(1)

QVector<T>

O(1)

O(n)

O(n)

Amort. O(1)

关联容器时间复杂度比较:


Key lookup

Insertion

Average

Worst case

Average

Worst case

QMap<Key, T>

O(log n)

O(log n)

O(log n)

O(log n)

QMultiMap<Key, T>

O(log n)

O(log n)

O(log n)

O(log n)

QHash<Key, T>

Amort. O(1)

O(n)

Amort. O(1)

O(n)

QSet<Key>

Amort. O(1)

O(n)

Amort. O(1)

O(n)


原始出处http://blog.csdn.net/xuguangsoft/article/details/8514921

//我是华丽的分割线///

四.指针

因为JAVA没有指针,下面探讨QT的智能指针

从内存泄露开始?

很简单的入门程序,应该比较熟悉吧 ^_^

#include <QApplication>
#include <QLabel>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QLabel *label = new QLabel("Hello Dbzhang800!");
    label->show();
    return app.exec();
}


在  从 Qt 的 delete 说开来 一文中,我们提到这个程序存在内存泄露(表现就是析构函数不被调用),而且当时给出了三种解决方法:

  • 将label对象分配到stack而不是heap中
  • 给label设置标记位Qt::WA_DeleteOnClose

  • 自己调用delete来删除通过new分配到heap中的 label 对象

注:

  • 为了能清楚观察构造和析构函数的调用,我们可以简单子类化了一下QLabel,然后用Label取代前面的QLabel
  • class Label :public QLabel
    {
    public:
        Label(const QString& text, QWidget *parent=NULL)
            :QLabel(text, parent){qDebug("from constructor");}
        ~Label(){qDebug("from destructor");}
    };


本文中,我们从智能指针(smart pointer)角度继续考虑这个问题

智能指针

为了管理内存等资源,C++程序员通常采用RAII(Resource Acquisition Is Initialization)机制:在类的构造函数中申请资源,然后使用,最后在析构函数中释放资源。

如果没有智能指针,程序员必须保证new对象能在正确的时机delete,四处编写异常捕获代码以释放资源,而智能指针则可以在退出作用域时(不管是正常流程离开或是因异常离开)总调用delete来析构在堆上动态分配的对象。

我们看看Qt家族的智能指针:

智能指针


引入

QPointer

Qt Object 模型的特性(之一)
注意:析构时不会delete它管理的资源


QSharedPointer

带引用计数

Qt4.5

QWeakPointer


Qt4.5

QScopedPointer


Qt4.6

QScopedArrayPointer

QScopedPointer的派生类

Qt4.6

QSharedDataPointer

用来实现Qt的隐式共享(Implicit Sharing)

Qt4.0

QExplicitlySharedDataPointer

显式共享

Qt4.4




std::auto_ptr

 

std::shared_ptr

std::tr1::shared_ptr

C++0x

std::weak_ptr

std::tr1::weak_ptr

C++0x

std::unique_ptr

boost::scoped_ptr

C++0x

注:

  • MSVC2010 和 GCC g++ 4.3 支持 C++0x
  • MSVC2008 sp1 及 GCC g++ 4.0 支持 tr1

有了这些东西,我们就可以很容易改造我们前面的例子了(只需改变一行):

std::auto_ptr<QLabel> label(new QLabel("Hello Dbzhang800!"));

根据你所用的Qt的版本,以及C++编译器的支持程度,你可以选用:

  • QScopedPointer
  • std::unique_ptr
  • QSharedPointer
  • std::shared_ptr
    • std::tr1::shared_ptr

QPointer

如何翻译呢?我不太清楚,保留英文吧。

  • The QPointer class is a template class that provides  guarded pointers   to QObjects.

  • 使用:一个guarded指针,QPointer<T> ,行为和常规的指针 T * 类似

  • 特点:当其指向的对象(T必须是QObject及其派生类)被销毁时,它会被自动置NULL.
    • 注意:它本身析构时不会自动销毁所guarded的对象
  • 用途:当你需要保存其他人所拥有的QObject对象的指针时,这点非常有用

一个例子

     QPointer<QLabel> label = new QLabel;
     label->setText("&Status:");
     ...
     if (label)
         label->show();


如果在...部分你将该对象delete掉了,label会自动置NULL,而不会是一个悬挂(dangling)的野指针。

QPointer 属于Qt Object模型的核心机制之一,请注意和其他智能指针的区别。

std::auto_ptr

这个没多少要说的。

  • 不能让多个auto_ptr 指向同一个对象!(auto_ptr被销毁时会自动删除它指向的对象,这样对象会被删除多次)
    • 通过拷贝构造或赋值进行操作时,被拷贝的会自动变成NULL,复制所得的指针将获得资源的唯一所有权
  • 智能指针不能指向数组(因为其实现中调用的是delete而非delete[])
  • 智能指针不能作为容器类的元素。

在C++0x中,auto_ptr已经不建议使用,以后应该会被其他3个智能指针所取代。

QScopedPointer 与 std::unique_ptr

它们概念上应该是是一样的。下面不再区分:

这是一个很类似auto_ptr的智能指针,它包装了new操作符在堆上分配的动态对象,能够保证动态创建的对象在任何时候都可以被正确地删除。但它的所有权更加严格,不能转让,一旦获取了对象的管理权,你就无法再从它那里取回来。

无论是QScopedPointer 还是 std::unique_ptr 都拥有一个很好的名字,它向代码的阅读者传递了明确的信息:这个智能指针只能在本作用域里使用,不希望被转让。因为它的拷贝构造和赋值操作都是私有的,这点我们可以对比QObject及其派生类的对象哈。

用法 (来自Qt的manual):

考虑没有智能指针的情况,

 void myFunction(bool useSubClass)
 {
     MyClass *p = useSubClass ? new MyClass() : new MySubClass;
     QIODevice *device = handsOverOwnership();

     if (m_value > 3) {
         delete p;
         delete device;
         return;
     }

     try {
         process(device);
     }
     catch (...) {
         delete p;
         delete device;
         throw;
     }

     delete p;
     delete device;
 }


我们在异常处理语句中多次书写delete语句,稍有不慎就会导致资源泄露。采用智能指针后,我们就可以将这些异常处理语句简化了:

另,

void myFunction(bool useSubClass)
 {
     QScopedPointer<MyClass> p(useSubClass ? new MyClass() : new MySubClass);
     QScopedPointer<QIODevice> device(handsOverOwnership());

     if (m_value > 3)
         return;

     process(device);
 }


我们一开始的例子,也是使用这两个指针的最佳场合了(出main函数作用域就将其指向的对象销毁)。

注意:因为拷贝构造和赋值操作私有的,它也具有auto_ptr同样的“缺陷”——不能用作容器的元素。

QSharedPointer 与 std::shared_ptr

QSharedPointer 与 std::shared_ptr 行为最接近原始指针,是最像指针的"智能指针",应用范围比前面的提到的更广。

QSharedPointer 与 QScopedPointer 一样包装了new操作符在堆上分配的动态对象,但它实现的是引用计数型的智能指针 ,可以被自由地拷贝和赋值,在任意的地方共享它,当没有代码使用(引用计数为0)它时才删除被包装的动态分配的对象。shared_ptr也可以安全地放到标准容器中,并弥补了std::auto_ptr 和 QScopedPointer 因为转移语义而不能把指针作为容器元素的缺陷。

QWeakPointer 与 std::weak_ptr

强引用类型的QSharedPointer已经非常好用,为什么还要有弱引用的 QWeakPointer?

QWeakPointer 是为配合 QSharedPointer 而引入的一种智能指针,它更像是 QSharedPointer 的一个助手(因为它不具有普通指针的行为,没有重载operator*和->)。它的最大作用在于协助 QSharedPointer 工作,像一个旁观者一样来观测资源的使用情况。

  • weak_ptr 主要是为了避免强引用形成环状。摘自msdn中一段话:
    • A cycle occurs when two or more resources controlled by shared_ptr objects hold mutually referencing shared_ptr objects. For example, a circular linked list with three elements has a head node N0; that node holds a shared_ptr object that owns the next node, N1; that node holds a shared_ptr object that owns the next node, N2; that node, in turn, holds a shared_ptr object that owns the head node, N0, closing the cycle. In this situation, none of the reference counts will ever become zero, and the nodes in the cycle will not be freed. To eliminate the cycle, the last node N2 should hold a weak_ptr object pointing to N0 instead of a shared_ptr object. Since the weak_ptr object does not own N0 it doesn't affect N0's reference count, and when the program's last reference to the head node is destroyed the nodes in the list will also be destroyed.
  • 在Qt中,对于QObject及其派生类对象,QWeakPointer有特殊处理。它可以作为QPointer的替代品
    • 这种情况下,不需要QSharedPointer的存在
    • 效率比 QPointer 高

QSharedDataPointer

这是为配合 QSharedData 实现隐式共享(写时复制 copy-on-write))而提供的便利工具。

Qt中众多的类都使用了隐式共享技术,比如QPixmap、QByteArray、QString、...。而我们为自己的类实现隐式共享也很简单,比如要实现一个 Employee类:

  • 定义一个只含有一个数据成员(QSharedDataPointer<EmployeeData>) 的 Employee 类

  • 我们需要的所有数据成员放置于 派生自QSharedData的 EmployeeData类中。

具体实现看 QSharedDataPointer 的Manual,此处略

QExplicitlySharedDataPointer

这是为配合 QSharedData 实现显式共享而提供的便利工具。

QExplicitlySharedDataPointer 和 QSharedDataPointer 非常类似,但是它禁用了写时复制功能。这使得我们创建的对象更像一个指针。

一个例子,接前面的Employee:

#include "employee.h"

 int main()
 {
     Employee e1(1001, "Albrecht Durer");
     Employee e2 = e1;
     e1.setName("Hans Holbein");
 }


写时复制技术导致:e1和e2有相同的工号,但有不同名字。与我们期待的不同,显式共享可以解决这个问题,这也使得Employee本身更像一个指针。

补遗

先前竟未注意到官方的这两篇文章(这是失败):
便看看google编码规范中对3个智能指针的建议: 
scoped_ptr
Straightforward and risk-free. Use wherever appropriate.
auto_ptr
Confusing and bug-prone ownership-transfer semantics. Do not use.
shared_ptr
Safe with const referents (i.e. shared_ptr<const T> ). Reference-counted pointers with non-const referents can occasionally be the best design, but try to rewrite with single owners where possible.

参考

因文章太长,下面将在《QT与JAVA的比较二》继续。。。


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值