For some reason I need to use string with fixed size. Now I'm looking on a QString class.
But there I have some questions with making QString object having constant size.
For example, I want to have string with size 10, it means, that if I would try to write some string with more than 100 chars in it, it would cut all the characters after 100 one.
I found constructor for QString in Qt docs, but I'm not sure if it would work as I told
QString( int size , QChar ch)
What can you suggest in such situation?
You can have a wrapper class that has a string, but is not a string, yet it can be used wherever a QString could be used. It can also be used with all QString's methods and operators, as long as you treat it like it were a pointer.
#include <QString>
class FixedWidthString {
mutable QString m_string;
//! Ignored if negative.
int m_maxLength;
inline const QString& data() const {
if (m_maxLength >= 0 && m_string.length() > m_maxLength)
m_string.truncate(m_maxLength);
return m_string;
}
inline QString& data() {
if (m_maxLength >= 0 && m_string.length() > m_maxLength)
m_string.truncate(m_maxLength);
return m_string;
}
public:
explicit FixedWidthString(int maxLength = -1) : m_maxLength(maxLength) {}
explicit FixedWidthString(const QString & str, int maxLength = -1) : m_string(str), m_maxLength(maxLength) {}
operator const QString&() const { return data(); }
operator QString&() { return data(); }
QString* operator->() { return &data(); }
const QString* operator->() const { return &data(); }
QString& operator*() { return data(); }
const QString& operator*() const { return data(); }
FixedWidthString & operator=(const FixedWidthString& other) {
m_string = *other;
return *this;
}
};
int main() {
FixedWidthString fs(3);
FixedWidthString fs2(2);
*fs = "FooBarBaz";
Q_ASSERT(*fs == "Foo");
fs->truncate(2);
Q_ASSERT(*fs == "Fo");
fs->append("Roo");
Q_ASSERT(*fs == "FoR");
fs->truncate(1);
*fs += "abc";
Q_ASSERT(*fs == "Fab");
fs2 = fs;
Q_ASSERT(*fs2 == "Fa");
}
Related
I get a blank output. I'm a newbie and have been struggling on this for some time.
I have gotten 0 errors by the compiler.
Also what can be improved on this?
How can I get the length of const char* as an int instead of size_t without having to use static_cast.
#include <iostream>
#include <cassert>
class String
{
private:
char* Str_Buffer{};
int Str_Size{};
public:
String(const char* string = " ")
: Str_Size{ static_cast<int>(strlen(string)) }
{
Str_Buffer = new char[Str_Size];
}
String& operator=(const String& string)
{
if (this == &string)
return *this;
delete[] Str_Buffer;
Str_Size = string.Str_Size;
if (string.Str_Buffer)
{
Str_Buffer = new char[Str_Size];
for (int index{ 0 }; index < Str_Size; ++index)
Str_Buffer[index] = string.Str_Buffer[index];
}
return *this;
}
char& operator[](const int index)
{
assert(index >= 0);
assert(index < Str_Size);
return Str_Buffer[index];
}
friend std::ostream& operator<<(std::ostream& out, const String& string)
{
out << string.Str_Buffer;
return out;
}
~String()
{
delete[] Str_Buffer;
}
};
int main()
{
String word("Hello world!");
std::cout << word;
return 0;
}
I get a blank output.
You don't fill your String::Str_Buffer with meaningful data in the constructor. You could use std::strcpy() from <cstring> to do that. std::strlen() is also declared in that header file. To use std::strcpy() the memory pointed to by String::Str_Buffer needs to be one char bigger than the string you want to copy there because strings in C and C++ are zero-terminated ('\0').
How can I get the length of const char* as an int instead of size_t without having to use static_cast.
Why would you want an int? Sizes of objects in C++ are measured with values of type std::size_t (defined in several headers but when in doubt include <cstddef>). std::size_t is guaranteed to be big enough to handle all object sizes. It is for example the return type of std::strlen() and the sizeof-operator.
Your assignment operator is not exception-safe:
String& operator=(const String& string)
{
// ...
delete[] Str_Buffer; // the old state is now gone
Str_Size = string.Str_Size;
if (string.Str_Buffer)
{
Str_Buffer = new char[Str_Size]; // when new[] throws, the object
// will be in an undefined state
// ...
Possible but not elegant solution:
String& operator=(const String& string)
{
char *temp = new[string.Str_Size];
// copy string.Str_Buffer to temp
delete[] Str_Buffer;
Str_Buffer = temp;
Str_Size string.Str_Size
return *this;
}
See Copy-and-Swap for an better solution.
Resource Management
Please familiarize yourself with The Rule of Five and the Copy-and-Swap Idiom.
A starting point for a class that manages a string could look like that:
#include <cassert> // assert()
#include <cstddef> // std::size_t
#include <cstring> // std::strlen(), std::strcpy()
#include <utility> // std::swap(), std::exchange()
#include <iostream>
class string_t
{
size_t length = 0;
char *data = nullptr;
public:
string_t() = default;
string_t(char const *str)
: length { str ? std::strlen(str) : 0 },
data { new char[length + 1]{} }
{
str && std::strcpy(data, str);
}
string_t(string_t const &other) // copy constructor
: length { other.length },
data { new char[length + 1]{} }
{
other.data && std::strcpy(data, other.data);
}
string_t(string_t &&other) // move constructor
: length { std::exchange(other.length, 0) }, // steal others resources and
data { std::exchange(other.data, nullptr) } // give other a state it's
{} // destructor can work with
string_t& operator=(string_t other) // assignment operator
{ // mind: other gets copied
std::swap(length, other.length); // steal other's resources
std::swap(data, other.data); // other's destructor will
} // take care of ours.
~string_t() { delete[] data; }
std::size_t get_length() const { return length; }
char& operator[](std::size_t index)
{
assert(index < length);
return data[index];
}
// stream-insertion operator:
friend std::ostream& operator<<(std::ostream &os, string_t const &str)
{
return os << (str.data ? str.data : "");
}
};
int main()
{
string_t foo{ "Hello!" }; // char const* constructor
std::cout << foo << '\n';
string_t bar{ foo }; // copy constructor
std::cout << bar << '\n';
string_t qux{ string_t{ "World!" } }; // move constructor (from a temporary)
std::cout << qux << '\n';
bar = qux; // assignment operator
std::cout << bar << '\n';
}
First of all, you need to include for strlen. You get a blank output because the constructor does not write the input string to Str_Buffer. You may use std::copy to copy the memory to the allocated buffer.
You have to use static cast, because strlen returns std::size_t. Just change the type of Str_Size to std::size_t to get rid of the static cast.
Also take a look at the rule of five. Defining a move and copy constuctor will improve performace of your code.
See a working version of your code below:
#include <iostream>
#include <cassert>
#include <cstring>
#include <algorithm>
class String
{
private:
char* Str_Buffer;
std::size_t Str_Size;
public:
String(const char* string = " ")
: Str_Size{ strlen(string) }
{
Str_Buffer = new char[Str_Size];
std::copy(string, string + Str_Size, Str_Buffer);
}
String(const String& other)
: Str_Size(other.Str_Size)
{
Str_Buffer = new char[Str_Size];
std::copy(other.Str_Buffer, other.Str_Buffer + Str_Size, Str_Buffer);
}
String(String && other)
{
*this = std::move(other);
}
String& operator=(const String& string)
{
if (this == &string)
return *this;
delete[] Str_Buffer;
Str_Size = string.Str_Size;
if (string.Str_Buffer)
{
Str_Buffer = new char[Str_Size];
for (std::size_t index = 0; index < Str_Size; ++index)
Str_Buffer[index] = string.Str_Buffer[index];
}
return *this;
}
char& operator[](const int index)
{
assert(index >= 0);
assert(index < Str_Size);
return Str_Buffer[index];
}
friend std::ostream& operator<<(std::ostream& out, const String& string)
{
out << string.Str_Buffer;
return out;
}
~String()
{
delete[] Str_Buffer;
}
};
int main()
{
String word("Hello world!");
std::cout << word;
return 0;
}
The QByteArray documentation states that it is to be used when memory conservation is critical. I've been working on a class (FieldName) that keeps an internal store of commonly used QString's so that when two FieldName objects are compared for equality, a quick integer comparison is done.
My thinking was that I could save some memory and gain speed by using QByteArray instead of QString since I only need standard ASCII characters. But when I change out the QString in the following code for QByteArray, my tests show that speed is increased, but memory usage more than doubles. This runs counter to the documentation.
#ifndef H_FIELDNAME
#define H_FIELDNAME
#include <QString>
class FieldName
{
public:
FieldName() : mKey(0) { init(); }
FieldName(const QString& s);
const QString& constString() const;
bool operator==(const FieldName& other) const { return mKey == other.mKey; }
bool operator!=(const FieldName& other) const { return mKey != other.mKey; }
private:
int mKey;
void init();
int findKey(const QString& s);
};
#endif
CPP file:
#include "fieldname.h"
// #include <QMutex> // demo version not thread-safe
#include <QVector>
static QVector<QString*> FieldName__List;
static bool FieldName__FirstCall(true);
FieldName::FieldName(const QString& s)
{
init();
mKey = findKey(s);
}
const QString& FieldName::constString() const
{
return(*FieldName__List.at(mKey));
}
void FieldName::init()
{
if(FieldName__FirstCall)
{
if(FieldName__List.isEmpty())
{
FieldName__List.append(new QString(""));
FieldName__FirstCall = false;
}
}
}
int FieldName::findKey(const QString& s)
{
if(s.isEmpty())
return 0;
int sz(FieldName__List.size());
for(int idx=1; idx<sz; ++idx)
{
if(s == *FieldName_List.at(idx))
return idx;
}
FieldName__List.append(new QString(s));
return(FieldName__List.size() - 1);
}
Can anyone let me know what I may be doing that is causing this to use more memory when I substitute QByteArray for QString in the above class?
Testcode:
QStringList qsl; // filled with 84000 unique English words
FieldName fn;
foreach(QString s, qsl)
{
fn = FieldName(s);
// do stuff here
}
I am trying to implement a custom data object for XML strings, so that if it exists in the clipboard I can parse that accordingly. The code for the XMLDataObject is as follows:
class XMLDataFormat : public wxDataFormat
{
public:
XMLDataFormat() : wxDataFormat(wxT("XMLDataFormat")) {}
};
class XMLDataObject : public wxDataObjectSimple
{
public:
XMLDataObject(const wxString& xmlstring = wxEmptyString) : wxDataObjectSimple(), m_XMLString(xmlstring)
{
SetFormat(XMLDataFormat());
}
size_t GetLength() const { return m_XMLString.Len() + 1; }
wxString GetXML() const { return m_XMLString; }
void SetXML(const wxString& xml) { m_XMLString = xml; }
// Must provide overloads to avoid hiding them (and warnings about it)
size_t GetDataSize() const
{
return sizeof(void*); //or return GetLength()
}
bool GetDataHere(void *buf) const
{
char* c = _strdup(m_XMLString.c_str());
buf = (void*)c;
return true;
}
bool SetData(size_t len, const void* buf)
{
char* c = (char*)buf;
std::string stdString(c, len);
m_XMLString << stdString;
return true;
}
private:
wxString m_XMLString;
};
I send the data to clipboard (when user clicks copy) in the following fashion:
wxDataObjectComposite* dataobj = new wxDataObjectComposite();
dataobj->Add(new XMLDataObject("XML"));
if (wxTheClipboard->Open()) wxTheClipboard->SetData(dataobj);
wxTheClipboard->Close();
To get the data from the clipboard:
if (wxTheClipboard->Open()) {
XMLDataObject xmlObj;
wxTheClipboard->GetData(xmlObj);
if (xmlObj.GetLength() != 0) wxMessageBox(xmlObj.GetXML());
}
wxTheClipboard->Close();
When user clicks paste, I get weird characters instead of the text "XML". I realized that in the functions bool GetDataHere(void *buf) const and bool SetData(size_t len, const void* buf) the address of buf is different. I am not sure but maybe that is how it should be since Clipboard owns the data or behind the scenes wxWidgets is doing something.
By the way, I am using VS 2015 on Windows 10 and using wxWidgets 3.1.0.
Any suggestions is appreciated.
The following code so far has worked correctly:
class XMLDataFormat : public wxDataFormat
{
public:
XMLDataFormat() : wxDataFormat(wxT("XMLDataFormat")) {}
};
class XMLDataObject : public wxDataObjectSimple
{
public:
XMLDataObject(const wxString& xmlstring = wxEmptyString) : wxDataObjectSimple(), m_XMLString(xmlstring)
{
SetFormat(XMLDataFormat());
}
size_t GetLength() const { return m_XMLString.Len() + 1; }
wxString GetXML() const { return m_XMLString; }
void SetXML(const wxString& xml) { m_XMLString = xml; }
size_t GetDataSize() const
{
return GetLength();
}
bool GetDataHere(void *buf) const
{
//char* c = _strdup(m_XMLString.c_str()); not needed as per suggestion
memcpy(buf, m_XMLString.c_str(), m_XMLString.Len()+1);
return true;
}
bool SetData(size_t len, const void* buf)
{
//char* c = new char[len + 1]; not needed as per suggestion
//memcpy(c, buf, len); not needed as per suggestion
m_XMLString = wxString::FromUTF8((const char*)buf); //changed as per suggestion
//delete c;
return true;
}
virtual size_t GetDataSize(const wxDataFormat&) const
{
return GetDataSize();
}
virtual bool GetDataHere(const wxDataFormat&, void *buf) const
{
return GetDataHere(buf);
}
virtual bool SetData(const wxDataFormat&, size_t len, const void *buf)
{
return SetData(len, buf);
}
private:
wxString m_XMLString;
};
Any suggestions as to improve it is again appreciated.
I've been pulling my hair out over a certain error that seems to be plaguing my program. I've attempted to search online for cases similar to mine but I can't seem to find a way to apply the other solutions to this problem. My issue is as follows: When I initially open the program it immediately stops responding and crashes. Debugging led me to find the error in question is "0xC0000005: Access violation writing location 0xCCCCCCCC". It seems to be tied to me assigning two attributes (sku_ and name_ ) as '\0'. If I change these values to anything else such as "" or even "\0" the program runs in visual studio, but will fail to compile elsewhere. Could someone help me understand where I am going wrong?
Product.h
#ifndef SICT_Product_H__
#define SICT_Product_H__
#include "general.h"
#include "Streamable.h"
#include <cstring>
namespace sict {
class Product : public Streamable {
char sku_ [MAX_SKU_LEN + 1];
char* name_;
double price_;
bool taxed_;
int quantity_;
int qtyNeeded_;
public:
//Constructors
Product();
Product(const char* sku, const char* name1, bool taxed = true, double price = 0, int qtyNeeded =0);
Product(Product& g);
~Product();
//Putter Functions
void sku(const char* sku) { strcpy(sku_,sku); };
void price(double price) {price_ = price;};
void name(const char* name);
void taxed(bool taxed) { taxed_ = taxed; };
void quantity(int quantity) { quantity_ = quantity; };
void qtyNeeded(int qtyNeeded) { qtyNeeded_ = qtyNeeded; };
//Getter functions
const char* sku() const { return sku_; };
double price() const { return price_; };
const char* name() const { return name_; };
bool taxed() const { return taxed_; };
int quantity() const { return quantity_; };
int qtyNeeded() const { return qtyNeeded_; };
double cost() const;
bool isEmpty() const;
Product& operator=(const Product& );
bool operator==(const char* );
int operator+=(int );
int operator-=(int );
};
double operator+=(double& , const Product& );
std::ostream& operator<<(std::ostream& os, const Product& );
std::istream& operator>>(std::istream& is, Product& );
}
#endif
Product.cpp
#include <iostream>
#include <cstring>
#include "Product.h"
namespace sict {
Product::Product() {
sku_[0] = '\0';
name_[0] = '\0';
price_ = 0;
quantity_ = 0;
qtyNeeded_ = 0;
}
Product::Product(const char* sku, const char* name1, bool taxed1, double price1, int qtyNeeded1) {
strncpy(sku_, sku, MAX_SKU_LEN);
name(name1);
quantity_ = 0;
taxed(taxed1);
price(price1);
qtyNeeded(qtyNeeded1);
}
double Product::cost() const {
if (taxed_ == true) {
return (price_ * TAX) + price_;
}
else
return price_;
}
bool Product::isEmpty() const{
if (sku_ == nullptr && name_ == nullptr && quantity_ == 0 && price_ == 0 && qtyNeeded_ == 0) {
return true;
}
else
return false;
}
Product::Product(Product& ex) {
sku(ex.sku_);
price(ex.price_);
name(ex.name_);
taxed(ex.taxed_);
quantity(ex.quantity_);
qtyNeeded(ex.qtyNeeded_);
}
Product& Product::operator=(const Product& g) {
sku(g.sku_);
price(g.price_);
name(g.name_);
taxed(g.taxed_);
quantity(g.quantity_);
qtyNeeded(g.qtyNeeded_);
return *this;
}
Product::~Product() {
delete [] name_;
}
void Product::name(const char* name) {
name_ = new char [strlen(name) + 1];
strcpy(name_, name);
}
bool Product::operator==(const char* right) {
if (sku_ == right) {
return true;
}
else
return false;
}
int Product::operator+=(int g) {
quantity_ = quantity_ + g;
return quantity_;
}
int Product::operator-=(int g) {
quantity_ = quantity_ - g;
return quantity_;
}
double operator+=(double& p, const Product& right) {
p = p + (right.cost() * right.quantity());
return p;
}
std::ostream& operator<<(std::ostream& os, const Product& g) {
return g.write(os, true);
}
std::istream& operator>>(std::istream& is, Product& g) {
return g.read(is);
}
}
The General.h file referenced in the header is just a list of constant values such as the "TAX" and "MAX_SKU_LEN" values.
The Streamable header contains pure virtual functions. I will list it here in case it is needed.
Streamable.h
#ifndef SICT__Streamable_H_
#define SICT__Streamable_H_
#include <iostream>
#include <fstream>
#include "Product.h"
namespace sict {
class Streamable {
public:
virtual std::fstream& store(std::fstream& file, bool addNewLine = true)const = 0;
virtual std::fstream& load(std::fstream& file) = 0;
virtual std::ostream& write(std::ostream& os, bool linear)const = 0;
virtual std::istream& read(std::istream& is) = 0;
};
}
#endif
Thank you very much in advance.
Product::Product() {
sku_[0] = '\0';
name_[0] = '\0'; // <---- writing via unitialized pointer
price_ = 0;
quantity_ = 0;
qtyNeeded_ = 0;
}
There's one possible source of your problems. You have defined a char pointer (a dynamic array or a C-string, that is) name_ in your class but you never allocate any memory for it in your constructor, before attempting to record a value via the pointer. Naturally, you get a write access violation.
Before assigning the value to char at index [0] you need to first allocate space for at least one element in your string, e.g. by doing name_ = new char [1]. Alternatively, you may choose to initialize the pointer itself to NULL (or nullptr) and use that to indicate that name_ has not yet been set.
I have this code for the class IdTask
class IdTask
{
public:
IdTask(QString host, QString id) : _host(host), _id(id) {}
IdTask(const IdTask & idT) : _host(idT._host), _id(idT._id) {}
QString host() const { return _host;}
QString id() const { return _id;}
inline bool operator ==(const IdTask& idT) const { return _host == idT.host() && _id == idT.id();}
inline bool operator <(const IdTask& idT) const { return _host < idT.host() && _id < idT.id();}
private:
QString _host;
QString _id;
};
And the class IdResult
class IdResult
{
public:
IdResult(IdTask idT, QString id) : _idTask(idT), _id(id) {}
IdTask idTask() const { return _idTask;}
QString id() const { return _id;}
bool operator < (const IdResult& idR) const { return _idTask < idR.idTask() && _id < idR.id();}
private:
IdTask _idTask;
QString _id;
};
I create a QMap like this :
QMap<IdResult, QString> mapIdResultInput;
mapIdResultInput.insert(IdResult(IdTask("localhost", "init"), "a"), "_a");
The problem is with the function contains.
mapIdResultInput.contains(IdResult(IdTask("b", "c"), "g"));
It return always true.
What is the problem with my code?
I would solve this in the following way: I would sort my IdResults first by IdTask and than, if IdTasks are equal, by id. Here is my implementation of your operators in the classes:
class IdTask
{
public:
[..]
bool operator ==(const IdTask& idT) const
{ return _host == idT.host() && _id == idT.id(); }
bool operator <(const IdTask& idT) const
{ return (_host < idT.host()) || (_host == idT.host() && _id < idT.id()); }
[..]
};
And
class IdResult
{
[..]
bool operator < (const IdResult& idR) const
{ return (_idTask < idR.idTask()) || (_idTask == idR.idTask() && _id < idR.id()); }
[..]
};
And the test
QMap<IdResult, QString> mapIdResultInput;
mapIdResultInput.insert(IdResult(IdTask("localhost", "init"), "a"), "_a");
bool b = mapIdResultInput.contains(IdResult(IdTask("b", "c"), "g")); // false
b = mapIdResultInput.contains(IdResult(IdTask("localhost", "init"), "a")); // true