happy coders!
I had a plan to try to learn some C++ today and so I thought I could take an old C assignment from a previous course and just do the same thing in C++. The assignment is to read music files and retrieve data from their ID3 tags and sort them in folders according to their artist, album and track title etc etc... this does not really matter but you know at least what I'm going for.
So I played around a little with sets and made my program receive an array of strings specifying different songs which it will loop the algorithm over.
In this next step I got stuck though due to how I tried to copy the behaviour of my old C program which was a struct containing three values being:
int size;
char *tag_name;
char *data;
but so far I have been unable to recreate this dynamic behaviour in C++ where I wish to only have the members defined but not initialised since I wanted to be able to change this data later on. Technically I can do this in a way where I get the data from the file before I create the tag, and therefore give the constructor their initial values and be done with it. But can I do it in the way I want to?
class Tag {
public:
std::string name;
std::string data;
int size;
Tag() {}
Tag(std::string n, std::string d, int s) : name(n), data(d), size(s) { }
void setData(std::string data) { this.data = data }
};
Since I've tried a billion combinations of pointers and whatnot (Googled loads) I just returned to the above and decided to ask you how to actually accomplish this.
My brained is completely mashed but consider the above psuedo code since I bet it is not correct in any way...
So my question is:
How do I define a class so that I get a dynamic string allocation for the members name and data? I was almost thinking of using some good old char* but the point of me trying this was to learn some C++, so I am forcing myself to go through this now.
If I understand your question correctly, your default constructor already takes care of this. Your std::strings will initialize to empty string "". You can assign a different value to
this string at any time.
If you really wanted to, you could change your default constructor to
Tag() : name(""), data(""), size(0) {}
Related
I'm learning to code c++ and I've come to this problem:
I have this struct:
struct storeData
{
string name;
string username;
string fav_food;
string fav_color;
}data[30];
And I need to check if two usernames are equal so I made this statement:
for(i=0;i<c;i++){
if(data[c].username.compare(data[i].username)==0){
cout<<"Username already taken"<<endl;
}
}
And it works well, the problem that I have is that I'm required to make a function let's call it: isTaken that returns the error message, so I can use it whenever I need to for example delete a username so I don't have to copy/paste the code again.
So I began looking for an answer for that, many forums present a way to send the whole struct like this:
void isTaken(struct storeData *data)
which I understand but because I'm using string is not working, so I guess it's because string is an object? I'm using the library <string> I'm sorry if I'm not being that clear at the moment, I'm looking for a way to use isTaken(data[c].user); but I don't know how to declare the function, I think is also because string is not the same as C string but I'm not really sure I've been looking for a solution and could not find it.
I tried: void isTaken(struct storeData *data) but I got an error saying that I can't convert std::string to basic_string which makes sense if I'm correct about string I tried converting string into c string but could not get anywhere. I'm open to suggestions/corrections because I want to improve my code, also I could not find the answer here, so If someone's got a link to a problem like this please let me know.
Thank you so much for you time, have a good day.
Do you mean an array of structs instead of a struct of arrays?
In the example you are giving I see only an array of structs each of which has multiple string objects in it. You see, a string is a class coming from std and I wouldn't call it an array. If you want to know how to pass an array to a function, you should read about it (I'm sure you can find such a question in SO). If you want to have an array within your struct, then the struct will take care of the memory of the array, but you should definitely read about constructors.
You got an error because you are passing an string argument to a function which requires struct pointer
void isTaken(struct storeData *data);
...
isTaken(data[c].user);
but what you actually need is to have a function which takes an array of your users, its size and username you want to check
bool IsUsernameTaken(struct storeData data[], int dataSize, const string &username){
for(int i = 0; i<dataSize; i++){
if(username == data[i].username)
return true;
}
return false;
}
A C string looks like this
data
A C++ string usually looks like this
size
capacity
ptr
|
v
data
or if using short string optimization and the string is short enough
size
data
data
all are zero terminated.
Making a shallow copy a C string only cost the copy of the pointer to it. Where a copy of a might cost just copying the 3 members and possible an allocation of data, which is not ideal, therefor most C++ functions use a reference to a string making the cost equivalent to the C string.
All code is untested.
bool Find(const std::string& target);
Making a deep copy of a C string would also cost an allocation.
In C++ you have many options to do a search, for your struct it could look like this. In case your member variables are private you must use an access function
auto found = std::find(std::begin(data), std::begin(data)+c, [&target](const storeData& auser) { return auser.GetName() == target });
return (found != std::begin(data)+c);
The first two parameters are the range that is search, not including the 2nd. A lambda is used to check the name, a free function with the right declaration would also do.
std::string& GetName() { return name; }
The higher C++ protection schemes would advice adding 2 consts to that in case you don't need to change name.
const std::string& GetName() const { return name; }
Meaning the returned string cant be changed and the 2nd says it wont change anything in your class. This const version would be required as I used a const storeData& auser in the lambda to satisfy the constness of the struct.
Im writing a class within c++, however I am not certain on how to create the setters and getters for the arrays (sorry for it being a basic question!) I am getting the following error:
expected primary expression before ']' token
Here is my code:
Class planet: public body
{
private:
string name[];
string star[];
public:
void nameSetter (string h_name[])
{
name[] = h_name[];
}
};
Once again I am sorry for such I silly question, I know I am not passing an index through, however, when I create an index it throws up a large amount of errors!
string name[];
This is not an array, it is a pointer. Use vectors instead:
#include <vector>
class planet: public body
{
private:
vector<string> name;
vector<string> star;
public:
void nameSetter (const vector<string> &h_name)
{
name = h_name;
}
};
Arrays in C++ have compile-time fixed sizes. You can't have a declaration like string name[]; because it leaves the size empty. You can't do that unless you provide an initialization list from which the size is determined.
In addition, array type arguments are transformed to pointer arguments. So your string h_name[] argument is actually a string* h_name.
name[] = h_name[];
This line doesn't make much sense. It's almost like you're trying to access elements of name and h_name without giving an index. Perhaps you were intending to assign the h_name array to the name array, like so:
name = h_name;
However, as we've just seen, h_name is actually a pointer. And in fact, you can't assign to an array anyway, so even if h_name were an array, this still wouldn't work.
You'll be much better off using a standard container like std::vector. It appears that you want dynamically sized arrays anyway, so this will make that easy.
Even though an answer has been selected, I think maybe the original question may have been misunderstood.
I think what the OP intended was that each instance of planet should have 1 name and 1 star; so the array notation he's used in his code is a misunderstanding on his part about arrays and strings. Based on this assumption I will continue.
When you declare
string name[];
I believe you just want to hold the name of 1 planet, in which case you don't need and array, you just need a single string.
ie
string name;
The same goes for star.
This would make the code
Class planet: public body
{
private:
string name;
string star;
public:
void nameSetter (const string& h_name)
{
name = h_name;
}
};
Problem statement : User provides some data which I have to store inside a structure. This data which I receive come in a data structure which allows user to dynamically add data to it.
Requirement: I need a way to store this data 'inside' the structure, contiguously.
eg. Suppose user can pass me strings which I have to store. So I wrote something like this :
void pushData( string userData )
{
struct
{
string junk;
} data;
data.junk = userData;
}
Problem : When I do this kind of storage, actual data is not really stored 'inside' the structure because string is not POD. Similar problem comes when I receive vector or list.
Then I could do something like this :
void pushData( string userData )
{
struct
{
char junk[100];
} data;
// Copy userdata into array junk
}
This store the data 'inside' the structure, but then, I can't put an upper limit on the size of string user can provide.
Can someone suggest some approach ?
P.S. : I read something about serializability, but couldnt really make out clearly if it could be helpful in my case. If it is the way to go forward, can someone give idea how to proceed with it ?
Edit :
No this is not homework.
I have written an implementation which can pass this kind of structure over message queues. It works fine with PODs, but I need to extend it to pass on dynamic data as well.
This is how message queue takes data:
i. Give it a pointer and tell the size till which it should read and transfer data.
ii. For plain old data types, data is store inside the structure, I can easily pass on the pointer of this structure to message queue to other processes.
iii. But in case of vector/string/list etc, actual data is not inside the structure and thus if I pass on the pointer of this structure, message queue will not really pass on the actual data, but rather the pointers which would be stored inside this structure.
You can see this and this. I am trying to achieve something similar.
void pushData( string userData )
{
struct Data
{
char junk[1];
};
struct Data* data = malloc(userData.size() + 1);
memcpy(data->junk, userData.data(), userData.size());
data->junk[userData.size()] = '\0'; // assuming you want null termination
}
Here we use an array of length 1, but we allocate the struct using malloc so it can actually have any size we want.
You ostensibly have some rather artificial constraints, but to answer the question: for a single struct to contain a variable amount of data is not possible... the closest you can come is to have the final member be say char [1], put such a struct at the start of a variably-sized heap region, and use the fact that array indexing is not checked to access memory beyond that character. To learn about this technique, see http://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html (or the answer John Zwinck just posted)
Another approach is e.g. template <size_t N> struct X { char data_[size]; };, but each instantiation will be a separate struct type, and you can't pre-instantiate every size you might want at run-time (given you've said you don't want an upper bound). Even if you could, writing code that handles different instantiations as the data grows would be nightmarish, as would the code bloat caused.
Having a structure in one place with a string member with data in another place is almost always preferable to the hackery above.
Taking a hopefully-not-so-wild guess, I assume your interest is in serialising the object based on starting address and size, in some generic binary block read/write...? If so, that's still problematic even if your goal were satisfied, as you need to find out the current data size from somewhere. Writing struct-specific serialisation routines that incorporates the variable-length data on the heap is much more promising.
Simple solution:estimate max_size of data (ex 1000), to prevent memory leak(if free memory & malloc new size memory -> fragment memory) when pushData multiple called.
#define MAX_SIZE 1000
void pushData( string userData )
{
struct Data
{
char junk[MAX_SIZE];
};
memcpy(data->junk, userData.data(), userData.size());
data->junk[userData.size()] = '\0'; // assuming you want null termination
}
As mentioned by John Zwinck....you can use dynamic memory allocation to solve your problem.
void pushData( string userData )
{
struct Data
{
char *junk;
};
struct Data *d = calloc(sizeof(struct data), 1);
d->junk = malloc(strlen(userData)+1);
strcpy(d->junk, userdata);
}
I've hit a wall with a program that uses embedded SQL to fetch rows from a database table, stores the row data in a struct, and then has that data processed with results being stored in another struct and pushed to a linked list. The struct that where the fetch data is stored is as follows:
struct rowstruct {
char *first;
char *last;
long amt;
} client;
and my struct that I use to store the processed data (and subsequently push as a node in my linked list) is like this:
struct mystruct {
char *firstN;
char *lastN;
long total;
} data;
My problem is that with each fetch loop that occurs, I need to copy the client.first and client.last values into data.firstN and data.lastN, but I can't get it to work. The following, using the assignment operator, just seems to be copying the pointer, rather than the value:
data.firstN = client.first;
data.lastN = client.last;
If I output data.firstN and data.lastN after the first iteration of my loop, the values appear correct, but after the second fetch iteration, the first node in my list will reflect the values from the second fetch, rather than the first.
strcpy will compile, but fails at runtime due to segmentation fault, which from reading on here is due to the char* being used, though I don't think I can use char[] or string when fetching the data using embedded SQL, so that seems like a dead end.
I'm sure there's a way to do this, and it's probably obvious to most here, but I'm at a loss. Any help would be appreciated.
Thanks!
If the code says to copy the pointer, that is exactly what happens.
Probably what you want is something along the lines of
data.firstN = strdup (client.first);
data.lastN = strdup (client.last);
There are more C++-ish ways of doing the same thing, but this should get you over the hump.
You don't need to re-declare the structure. Instead, you can declare data with
struct rowstruct data;
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 11 years ago.
So, I'm reading Schildt's book 3rd edition about C++ and I'm doing all examples, but I have some PHP background and when I tried some stuff it occurs that it can not be compiled this way.I saw the Schildt's solution, so I'll give what I've tried to do and how it's done in the book, what I need to know, is there any way to make it work adjusting my function?
Here's what I'm trying
class card {
char author[40];
//char book[30];
int count;
public:
void store(char *auth,int ct);
void show();
};
void card::store(char *auth,int ct){
&author = *auth;
count = ct;
}
int main(){
card ob1, ob2;
ob1.store('Tolkin',10);
ob2.store('Pratchet',3);
ob1.show();
ob2.show();
return 0;
}
And here's the Schildt's solution:
class card {
char author[40];
int count;
public:
void store(char *auth,int ct);
void show();
};
void card::store(char *auth,int ct){
strcpy(author, auth);
count = ct;
}
The quick fixes:
Instead of char author[40] use std::string.
store(const std::string& auth,int ct)
author = auth; (std::string has assignment operators)
ob1.store("Tolkin",10); (single quotes are for char-literals)
Give card::show() a body. You currently have just the declaration. And because show() does not mutate card, make it a const member function: void show() const;
The real fix (sounds lapidar, but is my serious, well-intentioned advice):
Get a good introduction to C++.
I think the basic misunderstanding here is the way that C/C++ handle "char arrays" as strings. The strcpy routine copies the contents of a string, where the = assignment operator (applied to char) copies a single character, or a pointer to the string. &author = *auth will look at the auth pointer, dereference it using *, and take the single char found there, then take the address of (&) your char[] named author, and try to change the address to the char value.
You could…
Use strcpy to copy the contents of the string (but, in new code, don't use strcpy, use strncpy instead!)
Store a pointer to the string provided (char* author in your class would be assigned as author = auth; but then, if auth is free()d or delete[]()ed later, you will have a pointer to memory that no longer contains your string, which is bad)
Use a C++ std::string object instead of a C-style char[] for your string, which will behave more like a PHP string would. std::string author could be copied from std::string auth using author = auth.
String-handling in C++ is a big subject, but you will definitely want to get a good understanding of the differences between "thing" and "pointer-to-thing" types … !
Also, in C++, you must use "" around strings, and '' around single chars. There is a lot less "magic" in a C/C++ "" string, though; only \x type escape sequences work (for example, there is no "$var" substitution available). In Perl/PHP/Bourne/... you use '' for non-escaped strings and "" for escaped strings; in C++, since char and char[]/std::string are different types, they use the punctuation differently.
Not really, no. For any given instance of card, author is stored at a specific, fixed, memory location relative to the rest of the object. (author is actually inside the object: if you do std::cout << sizeof(card) << std::endl; you'll see that card is 40 bytes, because all it contains is an array of forty characters.) &author = ... is trying to tell the compiler that author is now stored somewhere else, but there's no way to tell the compiler that, because you already promised that author is stored inside the card.
That said, you could change your declaration of author to be a true pointer, rather than an array:
char * author;
and then assign to it like so :
author = auth;
but that's not a good idea. Whenever you pass a pointer around, you have to keep track of where it's gone, to make sure that all of your pointers are always valid, and that you never lose a pointer to memory you need to de-allocate later.
you are mixing up some types:
PHP is not typed while c++ is.
you can t compile because you are trying to assign a pointer to a reference.
&author = *auth;
i suggest to read a lot of documentation about reference and pointers!!
cheers!