• 11663阅读
  • 19回复

要命的QString(与QString无关,另开贴讨论) [复制链接]

上一主题 下一主题
离线wvins
 
只看楼主 倒序阅读 楼主  发表于: 2009-03-16
在写阅读器的过程中遇到一个QString的问题,始终解决不了,基本类型就没有问题,一访问QString的成员就崩溃,
简单看了一下QString的实现代码,总结了一下QString存在的问题如下,向高手请教。
class MyClass
{
public: 
     MyClass(const QString &str = "") {m_string = str;}
     ~MyClass(){}

     const QString &myString() const {return m_string;}
private:
     QString m_string;
}
---------------------
这样的一个类定义居然存在这一个致命的缺陷,字符串的operator=()是浅拷贝...
还望高手指点,
你们在定义字符串成员的时候是怎么做的?
---------------------
class MyClass
{
public:
     MyClass(const QString &str=""):m_string(str){}
     ~MyClass(){delete m_string;}
    
     const QString *myString() const{return m_string;}
     const QString &myString() const{return *m_string;}
     //这么写需要operator=()的深拷贝支持,否则也存在问题。
private:
     QString *m_string;
}
----------------------
以上方案不是我希望的,所以希望高手指点。
有说错的地方还请高手指正!
[ 此帖被wvins在2009-03-17 20:15重新编辑 ]
只看该作者 1楼 发表于: 2009-03-16
QString::QString ( const QString & other )
Constructs a copy of other.
This operation takes constant time, because QString is implicitly shared. This makes returning a QString from a function very fast. If a shared instance is modified, it will be copied (copy-on-write), and that takes linear time.


class MyClass
{
public:
     MyClass(const QString &str = "") {m_string =QString(str);}
     ~MyClass(){}

     const QString &myString() const {return m_string;}
private:
     QString m_string;
}
离线wvins
只看该作者 2楼 发表于: 2009-03-16
If a shared instance is modified, it will be copied (copy-on-write), and that takes linear time.
------------
这句话靠谱吗?删除源的时候,有复制一份吗?
我不确定,今天晚上我写段代码测试一下。

MyClass(const QString &str = "") {m_string =QString(str);}
这句话和我的功能有差别吗?都是调用的operator=(),不过你提醒了我,
下面的构造方式是可行的:
MyClass(const QString &str = "") :m_string(str){}
-----------------------
但这样子治标不治本,存在赋值函数的时候,比如:
void setString(const QString &str){m_string = str;}
这是无法避免的。
只看该作者 3楼 发表于: 2009-03-16
QString构造时是给引用计数器+1,修改时是建立新的内存区域,然后原内存区域引用计数器-1,引用计数器=0就把原内存区域给清除了

所以,我不觉得有你担心的问题存在

之前不清楚=是否可以,看下文就知道=其实也行

Overview
A shared class consists of a pointer to a shared data block that contains a reference count and the data.
When a shared object is created, it sets the reference count to 1. The reference count is incremented whenever a new object references the shared data, and decremented when the object dereferences the shared data. The shared data is deleted when the reference count becomes zero.
When dealing with shared objects, there are two ways of copying an object. We usually speak about deep and shallow copies. A deep copy implies duplicating an object. A shallow copy is a reference copy, i.e. just a pointer to a shared data block. Making a deep copy can be expensive in terms of memory and CPU. Making a shallow copy is very fast, because it only involves setting a pointer and incrementing the reference count.
Object assignment (with operator=()) for implicitly shared objects is implemented using shallow copies.
The benefit of sharing is that a program does not need to duplicate data unnecessarily, which results in lower memory use and less copying of data. Objects can easily be assigned, sent as function arguments, and returned from functions.
Implicit sharing takes place behind the scenes; the programmer does not need to worry about it. Even in multithreaded applications, implicit sharing takes place, as explained in Threads and Implicit Sharing.

Implicit Sharing in Detail
Implicit sharing automatically detaches the object from a shared block if the object is about to change and the reference count is greater than one. (This is often called copy-on-write or value semantics.)
An implicitly shared class has total control of its internal data. In any member functions that modify its data, it automatically detaches before modifying the data.
只看该作者 4楼 发表于: 2009-03-16
Re:要命的QStrin
引用第2楼wvins于2009-03-16 16:48发表的  :
If a shared instance is modified, it will be copied (copy-on-write), and that takes linear time.
------------
这句话靠谱吗?删除源的时候,有复制一份吗?
我不确定,今天晚上我写段代码测试一下。
.......

删除源其实仅仅删除了引用啊
离线wvins
只看该作者 5楼 发表于: 2009-03-16
谢谢,
我粗略的看了一下源码,有Data结构体,联想到我遇到的问题猜想可能是因为浅拷贝。
刚才写了小段代码,验证了删除源的情况下,确实没有问题。
----------------------
#include <QString>

class MyClass
{
public:
    MyClass(const QString &str=""){m_String = str;}
    ~MyClass(){}

    void setString(const QString &str){m_String = str;}
    QString myString(){return m_String;}
private:
    QString m_String;
};
------------
#include <QApplication>
#include <QtDebug>
#include "MyClass.h"

void f(MyClass &mc)
{
    QString *str = new QString("zzzzzzzzzz");
    mc.setString(*str);
    delete str;
}

void f1(MyClass &mc)
{
    QString str("xxxxxxxxxxx");
    qDebug()<<mc.myString();
}

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    MyClass mc;
    f(mc);
    f1(mc);
    qDebug()<<mc.myString();
    return app.exec();
}
===============================
我修正Bug的幻想啊~
破灭了......
离线wvins
只看该作者 6楼 发表于: 2009-03-16
即然这样就把我的相关代码贴出来,
代码写的不好,高手有空的话帮忙看看,能不能看出点端倪来。
------------------
1. 构造Model代码
void FrameRSSItems::fillAllItems(QSqlQuery &query)
{
 rssModel->clear();//清空Model
 ListItem *item;
 while(query.next())
 {
  item = new ListItem;
  item->setValue(query.record().field("readed").value().toBool(),
        query.record().field("title").value().toString(),
        query.record().field("description").value().toString(),
        query.record().field("url").value().toString());
   rssModel->addItem(item);//添加项
 }
}
--------------------
2. 获取值
void FrameRSSItems::onDoubleClick(const QModelIndex & index)
{
 emit selectionChanged(index.data(Qt::UserRole + 1).toString());
 ListItem *item= static_cast<ListItem*>(index.internalPointer());
 QString str = item->caption();//这个地方出错,但是对于另外一个基本数据类型没有报错
 qDebug()<<str;
}
--------------------
3. 类定义
class ListItem
{
public:
 ListItem():m_caption(""),m_description(""),m_url(""){}
    const QString &caption() const;
    QString description() const;

 QString url() const;
 bool readed() const;
    void setValue(bool readed, const QString &caption, const QString &description, const QString &url);
 void setReaded(bool value);
 bool selected;
private:
    bool m_readed;
    QString m_caption,
   m_description,
   m_url;
};
4. 类实现
const QString & ListItem::caption() const
{
 return m_caption;
}

QString ListItem::description() const
{
 return m_description;
}

QString ListItem::url() const
{
 return m_url;
}
bool ListItem::readed() const
{
 return m_readed;
}
void ListItem::setValue(bool readed, const QString &caption, const QString &description, const QString &url)
{
    m_readed = readed;
    m_caption = caption;
    m_description = description;
 m_url = url;
 selected = false;
}
void ListItem::setReaded(bool value)
{
 m_readed = value;
}
----------------------------
5.以下信息用来在QTableView中显示,是正确的。。。
QVariant ListModel::data(const QModelIndex &index, int role) const
{
 if (!index.isValid())
  return QVariant();
 
 ListItem *item = static_cast<ListItem*>(index.internalPointer());
 
 if (index.column()==0)
 {
  switch(role)
  {
   case Qt::DisplayRole:
    return item->caption();
   case Qt::DecorationRole:
    if (item->readed())
     return readed;
    else
     return unRead;
   //这里需要添加自定义枚举变量
   //用于获取item->selected的值
   case Qt::UserRole:
    return item->readed();
   
   case Qt::FontRole:
    if (!item->readed())
     return *m_bold;
    return *m_normal;
   case Qt::CheckStateRole:
    if (item->selected)
     return Qt::Checked;
    else
     return Qt::Unchecked;
   case Qt::UserRole+1:
    return item->url();
   default:
    return QVariant();
  }
 }
 if (index.column()==1)
 {
  switch(role)
  {
   case Qt::DisplayRole:
    return item->description();
   case Qt::FontRole:
    if (!item->readed())
     return *m_bold;
    return *m_normal;
   case Qt::UserRole+1:
    return item->url();
   default:
    return QVariant();   
  }
 }  
 return QVariant();
}
只看该作者 7楼 发表于: 2009-03-16
给个笨办法
ListItem 继承QObject

ListItem *item= static_cast<ListItem*>(index.internalPointer());
这句换成
ListItem *item= qobject_cast<ListItem*>(index.internalPointer());
之后检测下item==NULL

怀疑你指针出错,这个可能性最大
离线sbtree
只看该作者 8楼 发表于: 2009-03-16
为什么不把 const QString &caption() const;改成QString caption() const;试试
windows 7 + VC++2008 + Qt4.5.2
离线wvins
只看该作者 9楼 发表于: 2009-03-16
试过,没用。
离线wvins
只看该作者 10楼 发表于: 2009-03-16
貌似static_cast转换没有发挥作用,我对于static_cast的理解还不够,
但这里我把转换改成
TreeItemData *item= static_cast<TreeItemData*>(index.internalPointer());
返回的item依旧不为NULL
---------------
另外,createIndex()函数的说明如下:
Note that when you are using a QSortFilterProxyModel its indexes have their own internal pointer. It is not advisable to access the internal pointer in the index outside of the model. Use the data() function instead.
不建议在Model之外使用internalPointer,因为如果你使用QSortFilterProxyModel 的话,他有其他的意思。使用data比较靠谱。
------
但我这里不只需要获取值,我还需要设置值...
这里还需要加深对Qt  MVC模式的认识。
只看该作者 11楼 发表于: 2009-03-16
static_cast不会返回null的呀……

dynamic_cast还有点像样
离线wvins
只看该作者 12楼 发表于: 2009-03-16
明天继续...
离线steinlee

只看该作者 13楼 发表于: 2009-03-17
here return const QString & is a reference and not a copy. It is same as m_string.
If you m_string is available, the return string can be used. Otherwise, your code will crash.

class MyClass
{
public:
     MyClass(const QString &str = "") {m_string = str;}
     ~MyClass(){}

     const QString &myString() const {return m_string;}
private:
     QString m_string;
}    

QString myString() const {return m_string;} should work.
Looking for remote C/C++ and Qt 兼职
离线steinlee

只看该作者 14楼 发表于: 2009-03-17
You may need to use dynamic_cast<ListItem*> instead of static_cast<ListItem*>.

引用第7楼都市无名者于2009-03-16 18:15发表的  :
给个笨办法
ListItem 继承QObject
ListItem *item= static_cast<ListItem*>(index.internalPointer());
这句换成
.......
Looking for remote C/C++ and Qt 兼职
离线wvins
只看该作者 15楼 发表于: 2009-03-17
我现在不确定是不是QString的问题了,

here return const QString & is a reference and not a copy. It is same as m_string.
If you m_string is available, the return string can be used. Otherwise, your code will crash.

这里面什么时候m_string会无效呢?应该是没有地方销毁了它。
离线foxyz

只看该作者 16楼 发表于: 2009-03-17
关于QString等Qt的类,我只能说,设计的非常好。
离线wvins
只看该作者 17楼 发表于: 2009-03-17
引用第16楼foxyz于2009-03-17 10:04发表的 :
关于QString等Qt的类,我只能说,设计的非常好。


同意。
-----------------
现在我的问题换了一种方式解决了,但为什么会出这个错误依旧不清楚。

似乎和QModelIndex的传递以及internalPointer的值有关,鉴于这篇文章改个名称不合适,
我决定还是另开一个帖子讨论下QModelIndex比较合适。

还希望各位高手能够指点我对于QModelIndex以及Qt中MVC模式的认识。
谢谢各位的积极参与,
尤其感谢 都市无名者 的无私帮助,谢谢!
离线foxyz

只看该作者 18楼 发表于: 2009-03-18
其实,QT大量使用了design pattern
,包括shared pointer技术,flyweight factory等等。
4.3之前的这种技术还是有点缺陷的。你如果有个类成员函数返回QString&的还是改成QString比较合适。
离线wvins
只看该作者 19楼 发表于: 2009-03-18
你如果有个类成员函数返回QString&的还是改成QString比较合适。
---------------
这种方式在什么情况下会出问题,我在讨论的开始已经指出这种可能行了。

不过没法子啊
病急乱投医...
快速回复
限100 字节
 
上一个 下一个