1

I have what I thought was something simple. Declarations in class header:

public:
    int index;          // just your vanilla integer
    QString *qspointer; // allocated array with space for 1000 pointers
    // allocation occurs first thing in class code: it's definitely  there

code:

index = 0;                        // works
qspointer[index] = new QString(); // causes error

alternate code

QString *qs;                      // works
qs = new QString();               // works
qspointer[index] = qs;            // causes error

The compile error in either case is:

"invalid conversion from QString to char"
"initializing argument 1 of 'QString& QString::operator=(char)"

it's a QString pointer. I have an array of QString pointers. I simply want to put the pointer in the array of pointers. This works fine with non-class items like int pointers, character pointers, etc. It's as if this isn't actually an array of QString pointers. But... nearby, I have this code:

qspointers[index].~QString(); // compiler likes this.

Which... if this wasn't an array of QString pointers... should cause an error. But it doesn't. I am so confused. :)

I'm clearly missing something fundamental; appreciate any pointers (hah)

fyngyrz
  • 2,458
  • 2
  • 36
  • 43
  • 2
    `QString*` pointer points to an array of `QString`, not an array of `QString*`. `qspointer[index]` is a `QString`. Why don't you just use `QStringList` or similar, anyway? – Igor Tandetnik Nov 15 '16 at 04:54
  • 1
    The fact that `qspointers[index].~QString();` compiles only proves that `qspointers[index]` is a `QString`. If it were a pointer, it'd fail to compile - you'd need `qspointers[index]->~QString();` – Igor Tandetnik Nov 15 '16 at 04:57
  • ph, bloddy foo. :) Thank you! Forest... trees.... – fyngyrz Nov 15 '16 at 04:58
  • make it an answer, obvious as it is, it IS the answer, and I will vote it up – fyngyrz Nov 15 '16 at 04:58
  • Also, to answer your question about QStringList, the reason is for performance. This is a realtime signal processing application. I need every cycle; direct access through a fast tree to the list is quicker. Also, this is the pattern for a number of types. Of course it's all working fine now. Thanks again. – fyngyrz Nov 15 '16 at 05:05
  • 2
    You're already using a `QString`: a class that will happily allocate behind your back. The container those strings are in is the least of your concern unless you have **hard benchmarking results to convince yourself that you're right**. A `QVector` is no slower than managing all the storage manually if you use it correctly. – Kuba hasn't forgotten Monica Nov 15 '16 at 15:04

2 Answers2

1

qspointer[index] = new QString();

qspointer is a pointer to QString(s), which, probably, points at an array and may be indexed. Thus, qspointer[index] is a QString. To be more precise, qspointer[index] expression returns a reference to QString. new QString(); returns a pointer, so what you are trying to do in this line looks like

QString *ptr = whatever;
QString s = ptr;

which is obviously an error. Analogically,

qspointer[index] = qs;

has the same problem.

I have an array of QString pointers.

Looks like you don't. QString *qspointer; is a pointer to an array of QString, not an array of pointers.

To make an array of pointers, you need something like this:

// Declaration:
QString **qspointer; // Pointer to pointers

// Allocation:
qspointer = new QString*[1000]; // Allocated array of 1000 pointers

// Putting a pointer into the array of pointers:
qspointer[index] = new QString();

Next,

qspointers[index].~QString();

compiles, because calling a member by . is allowed for an object or a reference.

Finally, calling a destructor explicitly is usually a bad idea. Use delete for heap-allocated objects and delete[] for arrays. Note that having a pointer, you can't determine if it points to a single object or an array.

Community
  • 1
  • 1
Sergey
  • 7,985
  • 4
  • 48
  • 80
1

Given that QStrings are implicitly shared value classes (and also support move semantics since Qt 4.8), and that we're using C++ and not C, and the year is 2016, the last thing you should be doing is messing with pointers to strings like it was 1980s.

Here's what you want:

QVector<QString> strings(1000); // 1000 empty strings
strings[0] = "Foo";
strings[1] = "Bar";
strings.push_back("Moo");
Q_ASSERT(strings.back() == "Moo");

This would also be true if you were using std::string:

QVector<std::string> strings(1000);
// etc.

If your array has a fixed size, you should use std::array instead:

std::array<QString, 1000> strings;
strings[0] = "Foo";
strings[1] = "Bar";

Note that none of the above code will be slower than the way you presumably intend to do it:

QString *strings[1000];
strings[0] = new QString("Foo");
strings[1] = new QString("Bar");

But that's not all, of course. You need to ensure that the strings don't leak, and - presumably - that any unallocated strings aren't just dangling pointers. And of course you don't want anyone attempting to copy that array blindly:

// DO NOT EVEN THINK OF DOING IT THIS WAY!
class MyClass {
  Q_DISABLE_COPY(MyClass)
  QString *strings[1000];
public:
  MyClass() { memset(&strings, 0, sizeof(strings)); }
  // Note: calling delete with a null pointer is perfectly safe
  ~MyClass() { for (auto string : strings) delete string; }
  void setString(int index, const QString & value) {
    if (!strings[index])
      strings[index] = new QString(value);
    else
      *strings[index] = value;
  }
  QString string(int index) const {
    return strings[index] ? *strings[index] : QString();
  }
};

That way of doing it is horrible absolutely uncalled for in 2016. If your platform is too old to use std::array, use QVarLengthArray: it is like std::array that can optionally dynamically resize itself. And store those strings by value, not by pointer!.

The in-place lifetime management of strings you're doing by calling the destructor etc. is harrowing and has absolutely no place outside of specialized classes that deal with resource management. What does it mean in plain-English terms? It means that if you have benchmarking results that show that normal containers are somehow too slow and you've determined that your algorithms aren't to blame but simply need a specialized container to support said specialized algorithms then you should write a custom container that manages the collection of strings. And then use that container in your code, with nastiness encapsulated away. In 99.9999% of cases, you will not want to implement any such container. The standard library and Qt containers are plenty clever. Use them appropriately and you should be all set.

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • Kuba, what I'm doing works fine and 100% meets my needs. *That makes it 100% correct*. But thank you for taking the time to post your position. – fyngyrz Nov 20 '16 at 21:17