I know I'm a little late, but let's take a look.
using namespace std;
There's no need for this. Really. You're already qualifying most of your names -- keep doing that.
class Book {
public:
char title[30];
Book(char *tit) {
strcpy(title,tit);
};
char *gettitle() {
return title;
}
};
As others have said, use std::string
. As others have not said: definitely use std::string
. You cannot sanely mix raw C strings and exceptions without smart pointers. In fact, mixing any dynamic allocation with exceptions without smart pointers is a great way to drive yourself insane, but in this case it's not even remotely justified as there's a class that does exactly what you want.
Now, let's see how Book
can be rewritten:
class Book {
std::string title;
public:
Book(std::string const& t) : title(t)
{}
std::string get_title() const {
return title;
}
};
Notice the funny syntax in the constructor. That's called a constructor-initializer, and it's a good idea to initialise your objects that way when possible -- it might be faster, and it makes it more explicit.
Notice that the string is passed by const reference. This means you don't copy it, but that you also can't change it -- you don't want to change it, so that's okay. It has another benefit, but I'll show that later.
Finally, we make get_title
const so that you can call it on a const instance of Book
. Not very important in your minimal code, but it pays off to be as const-correct as possible.
int main() {
Book my_book("aaa");
std::cout << my_book.get_title() << '\n';
Yay, you know about how to initialise objects that don't have a default constructor. That's a handy thing to know, and less common than I'd like to see it. Notice that we don't need the cast any more -- in fact, we don't even need to explicitly call std::string
s constructor. That's because std::string
can be constructed from a char const*
, which "aaa"
converts to. (If you're curious, "aaa"
isn't itself char const*
-- it's char const[4]
.)
You used std::endl
here, but there's no need for that. You can just output a newline character, and it'll get printed eventually -- std::endl
makes sure it gets printed immediately, but unless the program crashes, it'll be outputted equally soon without it as far as the user can tell.
std::vector<Book> vector_of_books;
for (int i=0; i<10; i++)
vector_of_books.push_back(Book("aaa"));
Here's code that does exactly what you were doing, but with Book
's new constructor and with no temporary. I think this is just as clear, but some may disagree. An even clearer way to do this is
std::vector<Book> vector_of_books(10, Book("aaa"));
That'll do the same thing, and describes the intent more clearly.
As an aside: you say that "aaa"+i
didn't work. That is indeed something that won't work, partly because C++ doesn't provide an all-that-easy way of joining a string together with something else. Your best bet would be "aaa" + boost::lexical_cast<std::string>(i)
, unless you wanted to go through the trouble of explicitly creating a stringstream
. There are also functions like atoi
and snprintf
, but those are generally less safe or harder to use.
for (int i = 0; i < 10; ++i) {
std::cout << vector_of_books[i].get_title() << '\n';
}
};
This code is perfectly fine to use -- I applied some cosmetic changes, but they don't particularly matter. Gabriel is suggesting you use iterators, but I don't think those make things any better:
for (std::vector<Book>::iterator it = vector_of_books.begin();
it < vector_of_books.end();
++it) {
std::cout << *it << '\n';
}
The code is much longer and the bonus in efficiency isn't going to matter. Granted, with C++11 you could change the std::vector<Book>::iterator
into auto
, and then it would be decent, but in C++11 you could also just do:
for (std::string const& book : vector_of_books)
std::cout << book << '\n';
If this looks confusing, don't worry about it; it's most important that you get the basics down (like using std::string
), and iterating is just a nice option that you can keep in mind for when it's more efficient/clearer.