I'm currently working in Stroustrup's "Principles and practices" book for learning how to program using C++. I'm at a part where they introduce Tokens and show how to use them. I was attempting to do a simple test to make sure I'm constructing the class correctly and using the tokens and vector correctly.
Below is an example of what I've done.
class Test{
public:
char kind;
double value;
Test(char ch)
:kind(ch), value(0){}
Test(char ch, double val)
:kind(ch),value(val){}
};
int main(){
vector<Test>testLoop;
for(char i = 'a'; i < 'k'; i++){
testLoop.push_back(i, 1);
}
cout << testLoop[5].kind << endl << testLoop[5].value;
}
What I'm trying to accomplish (I think) is having a vector of objects that both obtain the members of the class 'Test', do a loop that adds a few of them to the vector 'testLoop', (and puts 1 in all the value members) then prints out the value of testLoop[5].kind and testLoop[5].value.
The specific errors I'm getting are:
|25|error: no matching function for call to 'std::vector<Test>::push_back(char&, int)'|
candidate expects 1 argument, 2 provided
Any and all criticism and help is welcome! - Thankyou
std::vector::push_back takes an object of the type being stored in the vector. So in this case you would need
testLoop.push_back(Test(i, 1));
You can also use std::vector::emplace_back, which takes the type's constructor arguments and constructs an object in the vector directly:
testLoop.emplace_back(i, 1);
Class Test has two constructors.
The constrcutor with one parameter
Test(char ch)
:kind(ch), value(0){}
is called a conversion constructor because it in fact convert an object of type (in this case) char to an object of type Test
So you might call method push_back the following way
for(char i = 'a'; i < 'k'; i++){
testLoop.push_back( i );
}
In this case the compiler would use the conversion constructor because you did not write explicitly
for(char i = 'a'; i < 'k'; i++){
testLoop.push_back( Test( i ) );
}
so the compiler will do this implicitly.
However if you would declare the constructor with the function specifier explicit
explicit Test(char ch)
:kind(ch), value(0){}
then the compiler could not call it implicitly and would issue an error. In this case you have to specify the constructtor explicitly by yourself
for(char i = 'a'; i < 'k'; i++){
testLoop.push_back( Test( i ) );
}
If you use two arguments as in your loop
for(char i = 'a'; i < 'k'; i++){
testLoop.push_back(i, 1);
}
the compiler is not so smart and can not implicitly call the constructor that has two parameters. It is simpler for it to issue an error.
However you could use an initializer list provided that the compiler supports this feature of C++ 2011
for(char i = 'a'; i < 'k'; i++){
testLoop.push_back( { i, 1 } );
}
In this case the compiler could call implicitly the constructor with two parameters.
Related
I have just started learning C++ a few days back. I was given an assignment to demonstrate + operator overloading to concatenate two strings. I came up with this solution:
#include <iostream>
using namespace std;
class Strcpy{
private:
char* wrd;
int len;
public:
Strcpy();
Strcpy(char* );
void Display();
friend Strcpy operator + (Strcpy, Strcpy);
friend Strcpy concatinator(Strcpy, Strcpy);
};
Strcpy :: Strcpy(){
wrd = '\0';
len = 0;
}
Strcpy :: Strcpy(char* w){
int i; len = 0;
for(i = 0; w[i] != '\0' ; i++)
len ++;
wrd = w;
}
void Strcpy :: Display(){
cout << "\nOutput: " << wrd << " "<< len;
}
Strcpy operator + (Strcpy obj1, Strcpy obj2){
Strcpy temp;
int i;
temp.wrd = new char[obj1.len + obj2.len];
temp = concatinator(temp, obj1);
temp = concatinator(temp, obj2);
temp.wrd[temp.len] = '\0';
return temp;
}
Strcpy concatinator(Strcpy obj, Strcpy temp){
for(int i = 0; temp.wrd[i] != '\0'; i++)
{
obj.wrd[obj.len] = temp.wrd[i];
obj.len++;
}
return obj;
}
int main(){
Strcpy word, word_I("Hello"), word_II("World");
word = word_I + word_II;
word.Display();
return 1;
}
Some things to be noted:
deprecated conversion from string constant to 'char*' [-Wwrite-strings] I realize this is being caused because I am converting an immutable type to a mutable one but what alternative approach can I try to get rid of this.
I want to avoid using friend functions, but the overloaded operator needs two arguments which isn't possible if it remains a class member.
The following line works the same even if it is changed, why is this happening:
temp.wrd = new char[obj1.len + obj2.len];
//changed to
temp.wrd = new char[//any number here];
I want avoid using string functions if that is possible at all.
Whenever i try taking an input in the following form, it crashes:
char* Strcpy :: get(){
char* temp;
cin >> temp;
return temp;
}
int main(){
Strcpy word;
Strcpy word_I(word.get()), word_II(word.get());
word = word_I + word_II;
word.Display();
return 1;
}
Lastly, I would appreciate any help that would help me improve on the existing solution and some explanation so as to why it is better and the mistakes I am making.
deprecated conversion from string constant to 'char*' [-Wwrite-strings] I realize this is being caused because I am converting an immutable type to a mutable one but what alternative approach can I try to get rid of this.
You never modify *w, so you can use a pointer to const instead.
I want to avoid using friend functions, but the overloaded operator needs two arguments which isn't possible if it remains a class member.
The first argument of a member operator overload is the implicit this pointer. If you declare Strcpy Strcpy::operator+(const Strcpy&) const, it will be a binary operator. That said, the friend operator is probably a better approach.
The following line works the same even if it is changed, why is this happening:
temp.wrd = new char[obj1.len + obj2.len];
//changed to
temp.wrd = new char[//any number here];
It will work as long as "any number" is large enough to contain the entire string. If you write outside of the bounds, the behaviour is undefined.
Whenever i try taking an input in the following form, it crashes:
char* temp;
cin >> temp;
The stream extraction operator requires that a char* passed to it must point to an array sufficiently large to contain the user input. You forgot to initialize temp, so the requirement is not satisfied. As a result, the behaviour of the program is undefined. Solution: Allocate some memory and initialize temp to point to that memory.
The same bug occurs the constructor Strcpy(char*). You don't initialize this->wrd, but you dereference it. Therefore the behaviour is undefined. The solution is the same as above.
If I understood this right
I want to avoid using friend functions, but the overloaded operator
needs two arguments which isn't possible if it remains a class member.
your statement is wrong.
Sample for a binary + operator in a class:
#include <iostream>
class Int {
private: int _i;
public:
Int(int i = 0): _i(i) { }
Int operator + (const Int &i) const
{
return Int(_i + i._i);
}
int get() const { return _i; }
};
int main(int, char**)
{
Int i1(1), i2(2);
Int i;
i = i1 + i2;
std::cout << "i: " << i.get() << std::endl;
return 0;
}
Compiled and tested with gcc on cygwin:
$ g++ -o test-op-plus test-op-plus.cc
$ ./test-op-plus.exe
i: 3
I'm creating a method and one parameter of that method asks for a reference of an unsigned int, but I want to put a default value on that parameter. For example:
#include <iostream>
using namespace std;
class A {
public:
void sender();
private:
unsigned int score = 10;
};
class B {
public:
void receiver(unsigned int & score);
};
void A::sender() {
cout << "Before: " << score << endl;
B b;
b.receiver(score);
cout << "After: " << score << endl;
}
void B::receiver(unsigned int & score) {
score = 100;
}
int main() {
A a;
a.sender();
return 0;
}
Live demo: in here
The error happen when I do this:
void receiver(unsigned int & score = 10u);
And the compiler returns:
error: could not convert ‘10u’ from ‘unsigned int’ to ‘unsigned int&’
Live demo: in here
You cannot assign a literal1 to a non-const reference.
There are two scenarios where one will suit your situation:
You intend to modify the argument being passed to receiver()
If that is the case, the use a non-const reference (unsigned int & score) without the default parameter. In situations where you pass a literal or temporary object to it, then it will result in a compiler error.
a.receiver(10); // Error
The above wouldn't make any much sense considering that you want to modify that argument (you wouldn't see the modification if C++ had allowed that2).
You intend to just use the parameter in a read-only fashion
Just use plain, non-reference, unsigned int, because const unsigned int& score is just a pain to write. In cases where you determine that an object is expensive to copy, then that is the time where you should have the parameter a const reference.
UPDATE: There are some cases where you want to modify something, but that something may or may not exist. In cases like that, you may want to use a non-owning pointer as the parameter.
// Declaration
void receiver(unsigned int* score = nullptr);
void B::receiver(unsigned int* score) {
if(score) *score = 100;
}
...
a.receiver(); // Uses the default parameter
unsigned int x;
a.reciever(&x);
In this case, it only assigns to score when it points to some (assumed) valid variable. Pointers aren't that bad at all.
UPDATE 2: However, as #Potatoswatter have pointed out, you may be better off with function overloading.
void B::receiver() {
// Do something else
}
void B::receiver(unsigned int& score) {
score = 100;
}
You should use this in cases where you want your overloads to behave differently on different parameters.
However again, I prefer the first, non-default parameter option, rather than the pointer option and the overloading option, as it requires that the caller provide a parameter, which is much better when you are modifying something through a function.
UPDATE 3: You should also consider to have your function return the value instead of modifying it through a parameter. In cases where you don't need the current state of the object being modified, having a function return a value is much more intuitive. One caveat though is that the caller may forget to capture (assign) the return value, which could be dangerous if you are using that value as some resource ID to free something up.
1 And in general, a temporary object.
2 And the universe would probably blow up if 10 was magically transformed into 100 ;)
You want the argument type to be const unsigned int&. Otherwise, you can do something crazy, like try to assign 10 = 20, which doesn't make sense.
And that happens to be exactly that you did. The score = 100 line doesn't seem to be what you actually meant.
the value "10" isn't a reference- it's a value. By having a by-reference parameter, it must be called with a reference. Using a default parameter means you could call the function without specifying the parameter and the compiler would use the default.
Similarly, calling b.receiver(10); is not valid, but
int someInt = 10;
b.receiver(someInt);
is valid.
I am attempting to initialize variables within my object, using a function with const pointers as parameters.
I keep getting errors in many of the ways i attempted, here is my code:
class Molecule
{
private:
char s[21];
char d[21];
double w= 0;
public:
Molecule();
void set(const char*, const char*, double);
void display() const;
};
int main() {
int n;
cout << "Molecular Information\n";
cout << "=====================" << endl;
cout << "Number of Molecules : ";
cin >> n;
Molecule *molecule = new Molecule[n];
for (int i = 0; i < n; i++) {
char symbol[21];
char description[21];
double weight;
molecule[i].set(&symbol,&discription,weight);
//...
}
//implementation of class
#include "Molecule.h"
#include <iostream>
#include <cstring>
void Molecule::set(const char*, const char*, double)
{
s = &symbol;
d = &discription;
w = &weigth;
}
My question is: How would i correctly call the member function from an array of objects, using constant chars as parameter, and what is the correct way to set them to my variables in my class.
P.S: I have been trying to figure this out for a long time, and posting here is a last resort.
There are multiple errors in your code
&symbol (where symbol is char[21]) yields char(*)[21], use symbol directly and let it decay to char* or use explicitly &symbol[0]
double weight; is uninitialized local variable, using it results in undefined behavior - you should initialize it: double weight = 0.0;
double w= 0; used to declare a member of class Molecule is invalid, you could use constructor's initializer list:
Molecule() : w(0.0) { } // initializes `w` to `0.0`
s = symbol; where s is char[21] and symbol is char* will not copy strings, for C-style copying strcpy could be used (note that C and C++ are different languages)
you have called new[] so it would be nice and appropriate to call delete[] as well and instead of relying on OS cleaning it up after the program terminates: (otherwise follow the point 6)
Molecule *molecule = new Molecule[n];
...
delete[] molecule;
If you are allowed to use vectors, replace Molecule *molecule = new Molecule[n]; with std::vector<Molecule> molecules(n);
If you are allowed to use std::string1) objects, replace char[21] / char* with std::string objects
Other suggestions:
use meaningful names for variables, if you want to explicitly distinguish private members from other local variables, good convention is to use _ at the end of the name:
class Molecule {
private:
std::string symbol_;
std::string description_;
double weight_;
1) Basically what you need to know about std::string is that it is a template that wraps raw char* and it already contains well-defined copying, concatenation using operator + and most important: you don't need to bother with memory management. Just #include <string>
In the call
molecule[i].set(&symbol,&discription,weight);
you are passing a pointer to a char array. This does not match the char* that set expects.
The easiest/best fix is to change this to
molecule[i].set(symbol,description,weight);
relying on the symbol and description char arrays automatically decaying to pointers.
Alternatively, you could also write
molecule[i].set(&symbol[0],&description[0],weight);
to explicitly pass char*
[Note that there are many other errors in the code posted. Based on the question, I'm guessing they are just typos. Please update your question if you'd like more info onn any of the other errors.]
#include "average.c++"
#include "name.c++"
class Grade {
public:
Grade() {}
void searcharray(Name *array[]) {
int i;
for(i = 0; i <= 10; i++){
printf("%s", array->name);
}
}
};
int main() {
int i;
char line[64];
Name *names[10];
for(i = 0; i < 5; i++){
scanf("%s", &line);
names[i] = new Name(line);
}
Grade *test;
test = new Grade();
test->searcharray(names);
}
This code gives the error
"grade.c++ in member function 'void Grad::searcharray(Name*)':
grade.c++:11:25: error: request for member 'name' in ' array', which is of pointer type 'Name*' (maybe you meant to use '->' ?)"
I need help making this work. I am guessing it is something simple like extending the class like you would in Java but not sure how this works in c++.
I am assuming you can pass an array of objects to a class like you would in C with just an array.
The root to my question is to find a solution and to get a reason for this code being wrong.
Your code can be substantially improved by taking advantage of the Standard library. The problem with your initial code was that you were doing array->name where array was a C-style array (technically the pointer into which it decayed). An expression like that isn't possible unless you obtain the pointer at the index first:
array[i]->name;
Moreover, the for loop in which that line was written is traversing the array 1 too many times. The conditional statement i <= 10 should be i < 10 so you won't dereference an address past the end of the array.
Anyway, instead of showing your code with the corrections, I thought I might as well show you what it should look like if you use vectors, memory-management, and std::string. I hope this helps:
#include <iostream>
#include <string>
#include <vector>
#include <memory>
class Grade
{
public:
Grade() { }
static void searcharray(const std::vector<std::unique_ptr<Name>>& array)
{
for (const auto& obj : array)
{
std::cout << obj->name;
}
}
};
int main()
{
std::string name;
std::vector<std::unique_ptr<Name>> names;
while (std::cin >> name)
names.push_back(std::unique_ptr<Name>(new Name(name)));
// names.push_back(std::make_unique<Name>(name))
Grade::searcharray(names);
}
Note that I also made searcharray static since it has nothing to do with a given instance of Grade.
As others have pointed out the problem is that you're using a parameter declared Name *array[] like array->name.
Remember that C++ built on top of C, which follows a rule 'declaration mimics use', which means that the way a variable is declared looks like the way it is used. So with the declaration:
Name *array[]
The way you get a name out of this is:
*array[i]
And name is a member of Name so you have to get a Name object first. Then you can tack on member access:
(*array[i]).name
And then you can use the -> shortcut where (*x).y is the same as x.y:
array[i]->name
Other issues:
Your code appears to be heavily influenced by the style of code required for the 1989 or 1990 version of C. You should try to avoid that as it makes writing C++ code much worse than it has to be.
You declare a Grade * and allocate it immediately. You can combine the declaration with initialization into:
Grade *test = new Grade();
But you don't need to use a pointer anyway: use Grade test; (and if you did need a pointer then you should use a smart pointer. Never use 'naked' new.)
Similarly you can avoid new when you create Names.
Name names[10]; // assuming that Name is default constructible
for(...) {
...
name[i] = Name(line);
}
You should avoid a fixed size array here. Instead you should default to using std::vector:
std::vector<Name> names;
for (...) {
...
names.push_back(Name(line)); // or in C++11 names.emplace_back(line);
}
You should declare the variable i as part of the for loop, not as a variable outside it:
for (int i=0; i<10; ++i)
When you read input you should avoid scanf and fixed sized buffers. Instead, if you're reading lines you should probably start off with std::getline and std::string.
std::string line;
while (std::getline(std::cin, line)) { // read as many lines as there are, not just 10 no matter what
names.emplace_back(line);
}
I wrote a simple test program in c++ but why does this crash on:
s[i] = s[i] - 'a' + 'A';
with the exception: Access violation writing location 0x01327808
#include "stdafx.h"
#include <iostream>
using namespace std;
class String
{
public:
char *s;
int len();
void upper();
String(char*);
};
String::String(char*x)
{
s = x;
}
int String::len()
{
return strlen(s);
}
void String::upper()
{
for (int i = 0; i < len(); i++)
{
if (s[i] >= 'a' && s[i] <= 'z')
{
cout << s[i] << endl;
s[i] = s[i] - 'a' + 'A';
}
}
};
int main()
{
String s("test");
s.upper();
cout << s.len() << endl;
cout << s.s << endl;
system("pause");
}
It's because of:
String s("test");
What this does is to pass the const char * "test" to your constructor which then simply stores the address of that string.
Later, when you try to modify the memory that the pointer points to, that's undefined behaviour.
Typically, string literals will be stored in read-only memory to allow certain optimisations to take place, and any attempt to modify them will result in a access violation.
If you were to change:
String s("test");
into:
char cp[] = "test";
String s(cp);
you may well find it will work.
However, your class should really be making a copy of the string for its own purposes - a mere pointer is unsafe, since the code that passed you that pointer can change the contents of it as well.
Things you should change in your code to make it safer:
s should be a private member, not a public one.
your constructor should make its own copy of the string:s = new char[strlen(x)+1];strcpy (s,x);.
add a destructor to take care of that as well:String::~String() { delete[] s; }.
consider having your constructor receive a const char * (since you're not changing it).
consider using toupper(ch) instead of ch - 'a' + 'A'. While your formula works for ASCII, I don't believe it's guaranteed by the standard.
cout stuff should be handled by the class rather then code outside of it (this will be mandatory once you make s private.
consider having a no-argument constructor so that string arrays will work okay.
String literals are constant (you're exploiting a deprecated automatic conversion to char *), and you're trying to modify one inside your class, which is undefined behavior.
In practice, the access violation happens because newer versions of VC++ put string literals in a section of the executable which is mapped in memory as read-only (which is definitely a good thing), and any attempt to write it results (correctly) in an access violation.
Solution: copy the string passed to the constructor (which, by the way, should take a const char *) in a buffer local to your class, probably dynamically allocated. In this last case, if you don't want to implement a copy constructor and an assignment operator, make them private thus avoiding that the default ones are executed, otherwise you'll get troubles (double frees and other nasty stuff) if you create copies of a String object.
Better solution: in real projects, don't reinvent the wheel and use a good pre-made string class like std::string/CString/wxString/whatever the framework you're using provides you.
String::String(char*x)
{
s = x;
}
You shouldn't copy string literals address just like above. Behavior is undefined.
you need to allocate memory and copy it.
size_t len = strlen(x);
s = new char[len+1];
strcpy(s,x);
Make sure to delete it in destructor of your String class.
You tried to assign to a string literal. They are not char*, they are const char*. Attempting to modify a string literal is undefined behaviour.
"test" is a const string, you cannot write to it.
Instead of
s = x;
try doing strcpy, so that s will have a non-const copy of x, rather than the const original.
You cannot modify "test" string by accessing with [].
The error is s[i] = something.
Infact, char* is only a pointer to an IMMUTABLE string.
In the constructor you must allocate space internally for handle "test" string literal and then you can modify single character accessing with [] operator.
Example to solve (with basic copyng):
String::String(char *original)
{
size_t len = strlen(original) + 1;
s = new char[len];
for ( size_t i = 0; i < len; ++i )
{
s[i] = original[i];
}
}