I'm getting back into writing some C++ and I'm honestly rusty. I get the feeling I'd find a quick answer to my question if I just knew how to phrase it properly, but still I'd appreciate your help.
sanitycheck.cpp:
#include <string>
using namespace std;
typedef struct STR_1 {
int val_a, val_b;
STR_1 (int a, int b)
{ val_a = a; val_b = b; }
} STR_1;
typedef struct STR_2{
string name;
STR_1 myStr1;
STR_2 (string n, STR_1 s)
{ name=n; myStr1 = s; }
} STR_2;
int main(){
return 0;
} // end main
When I try to compile with g++ -o sanitycheck ./test/sanitycheck.cpp I get the following,
./test/sanitytest.cpp: In constructor ‘STR_2::STR_2(std::string, STR_1)’:
./test/sanitytest.cpp:25:3: error: no matching function for call to ‘STR_1::STR_1()’
{ name=name; myStr1 = &s; }
^
./test/sanitytest.cpp:25:3: note: candidates are:
./test/sanitytest.cpp:11:3: note: STR_1::STR_1(int*, int*)
STR_1 (int *a, int *b)
^
./test/sanitytest.cpp:11:3: note: candidate expects 2 arguments, 0 provided
./test/sanitytest.cpp:7:16: note: STR_1::STR_1(const STR_1&)
typedef struct STR_1 {
^
./test/sanitytest.cpp:7:16: note: candidate expects 1 argument, 0 provided
./test/sanitytest.cpp:25:23: error: no match for ‘operator=’ (operand types are ‘STR_1’ and ‘STR_1*’)
{ name=name; myStr1 = &s; }
^
./test/sanitytest.cpp:25:23: note: candidate is:
./test/sanitytest.cpp:7:16: note: STR_1& STR_1::operator=(const STR_1&)
typedef struct STR_1 {
^
./test/sanitytest.cpp:7:16: note: no known conversion for argument 1 from ‘STR_1*’ to ‘const STR_1&’
One thing I'm not clear on is why would STR_1 myStr1; of STR_2 need to call the STR_1 constructor in the first place? Couldn't I initialize both types with,
int main()
{
STR_1 bob = STR_1(5,6);
STR_2 tom = STR_2('Tom',bob);
return 0;
}
Thanks!
Unless it is not already clear from the link in the comments to the OP: this here,
typedef struct STR_2{
string name;
STR_1 myStr1;
STR_2 (string n, STR_1 s) // here the myStr1 default constructor is called
{ name=name; myStr1 = s; }
} STR_2;
requires STR_1 to be default constructible. In order to work around, you have to construct the member STR_1 myStr1; in the constructor's initializer list:
STR_2 (string n, STR_1 s) : name(n), myStr1(s) {}
DEMO
This calls the compiler generated copy-constructor of STR_1 instead of the default constructor (the automatic generation of which is suppressed by providing a custom constructor).
Another option would be to use a pointer to STR_1:
typedef struct STR_2{
string name;
std::unique_ptr<STR_1> myStr1;
STR_2 (string n, STR_1 s)
{ name=name; myStr1 = std::make_unique<STR_1>(s); } //just for the sake of explanation
//again, this would be better
//done in the initializer list
} STR_2;
Yet, I would depart from the first alternative only for a good reason.
Related
class A {
int x;
std::string s;
public:
A(std::string _s): x(0), s(_s) {
std::cout << "\tA(string)\n" ;
}
A(int _x): x(_x), s("") {
std::cout << "\tA(int)\n" ;
}
A(const A &other): x(other.x), s(other.s) {
std::cout << "\tA(A& other)\n" ;
}
};
int main() {
std::string str = "Hello";
A obj_1(str);
A obj_2 = str;
A obj_3(10);
A obj_4 = 10;
char ch = 'a';
A obj_5 = ch; // How is this working?
// A obj_6 = "Hello"; // And not this?
const char *ptr = "Hello";
// A obj_7 = ptr; // or this?
return 0;
}
On executing this code, the output is:
A(string)
A(string)
A(int)
A(int)
A(int)
As far as I understand, obj_1 and obj_3 are created directly by using the respective ctors. For obj_2 and obj_4, the compiler does an implicit conversion by calling their respective ctor (so only 1 implicit conversion is required in each case). However, for obj_5 the compiler first has to convert from char to int and then one more implicit conversion to call the int-ctor. But C++ standard allows only 1 implicit conversion. So what is happening here?
Also, in that case "Hello" should first get converted to a std::string then one more implicit conversion to call the string-ctor. But this doesn't work.
But C++ standard allows only 1 implicit conversion.
Thats not correct.
From cppreference:
Implicit conversion sequence consists of the following, in this order:
zero or one standard conversion sequence;
zero or one user-defined conversion;
zero or one standard conversion sequence.
From the language point of view, const char[N] -> std::string (or const char* to std::string) is a user-defined conversion. Hence, the commented out lines are errors. On the other hand,
A obj_5 = ch; // How is this working?
is fine, because there is only a single user-defined conversion involved.
A solution is to convert "Rosie" to char* using (char*), I am curious if it is another one.
First, note that your default parameter value (c = new char[1]()) is a memory leak, since the constructor doesn't take ownership of the new[]'ed memory to delete[] it later. There is never a good reason to use new[]'ed memory for a parameter's default value.
"Rosie" is a string literal. It has a type of const char[6], which in C++11 and later cannot be assigned as-is to a non-const char* pointer, an explicit type-cast is required (in which case, you should use const_cast instead of a C-style cast), eg:
#include <iostream>
#include <string>
class Autoturism
{
static int nr_autoturisme; // nr_autoturis 'active'
char* culoare;
unsigned int a_fabricatie;
public:
Autoturism(char* = nullptr, unsigned int = 0);
~Autoturism();
// TODO: you will also need a copy constructor and a
// copy assignment operator, per the Rule of 3/5/0:
// https://en.cppreference.com/w/cpp/language/rule_of_three
...
};
int Autoturism::nr_autoturisme{ 0 };
Autoturism::Autoturism(char* c, unsigned int an)
{
if (!c) c = const_cast<char*>("");
size_t len = strlen(c);
culoare = new char[len + 1];
strcpy_s(culoare, len + 1, c);
an_fabricatie = an;
++nr_autoturism;
std::cout << "\nConstructorul a fost apelat !";
}
Autoturism::~Autoturism()
{
delete[] culoare;
std::cout << "\nDeconstructorul a fost apelat !";
}
...
int main()
{
Autoturism a1;
Autoturism a2(const_cast<char*>("Rosie"), 1999);
...
return 0;
}
Otherwise, if you really intend to stay with C-style string handling, then you should change the c parameter to const char* instead (it should be a pointer-to-const anyway, since the constructor does not modify the data being pointed at), eg:
#include <iostream>
#include <string>
class Autoturism
{
static int nr_autoturisme; // nr_autoturis 'active'
char* culoare;
unsigned int a_fabricatie;
public:
Autoturism(const char* = "", unsigned int = 0);
~Autoturism();
// TODO: you will also need a copy constructor and a
// copy assignment operator, per the Rule of 3/5/0:
// https://en.cppreference.com/w/cpp/language/rule_of_three
...
};
int Autoturism::nr_autoturisme{ 0 };
Autoturism::Autoturism(const char* c, unsigned int an)
{
if (!c) c = "";
size_t len = strlen(c);
culoare = new char[len + 1];
strcpy_s(culoare, len + 1, c);
an_fabricatie = an;
++nr_autoturism;
std::cout << "\nConstructorul a fost apelat !";
}
Autoturism::~Autoturism()
{
delete[] culoare;
std::cout << "\nDeconstructorul a fost apelat !";
}
...
int main()
{
Autoturism a1;
Autoturism a2("Rosie", 1999);
...
return 0;
}
But, with that said, why are you using this old C-style string handling in C++ at all? You should be using std::string instead (you are already including the <string> header), just let it deal with all of the memory management for you, eg:
#include <iostream>
#include <string>
class Autoturism
{
static int nr_autoturisme; // nr_autoturis 'active'
std::string culoare;
unsigned int a_fabricatie;
public:
Autoturism(const std::string & = "", unsigned int = 0);
// std:string is already compliant with the Rule of 3/5/0,
// so the compiler's auto-generated destructor, copy constructor,
// and copy assignment operator will suffice...
};
int Autoturism::nr_autoturisme{ 0 };
Autoturism::Autoturism(const std::string &c, unsigned int an)
{
culoare = c;
an_fabricatie = an;
++nr_autoturism;
std::cout << "\nConstructorul a fost apelat !";
}
int main()
{
Autoturism a1;
Autoturism a2("Rosie", 1999);
...
return 0;
}
String literals in C++ have types of constant character arrays that used as expressions with rare exceptions are converted to pointers to their first characters of the type const char *. But the first parameter of your constructor has the type char * instead of const char *. So the compiler issues an error.
Also the constructor can produce a memory leak due to the default argument where a memory is dynamically allocated but not deleted.
You could declare the constructor at least the following way
Autoturism( const char * = "", unsigned int = 0 );
As the memory is allocated dynamically you need explicitly to define the destructor that will delete the allocated memory and the copy constructor and the copy assignment operator or to define the last two as deleted.
In my comp sci class we are making a vector class. I am stuck because my copy constructor is initializing my 'limit' variable with a garbage value and I dont understand why.
Here is my main:
myStringVector v1 {"a", "b", "c"};
std::cout << "make it here?" << std::endl;
myStringVector v2 = v1;
std::cout << v1[0] << v1[1] << v1[2] << std::endl;
std::cout << v2[0] << v2[1] << v2[2] << std::endl;
assert(v1 == v2);
std::cout << "Passed copy ctor" << std::endl;
Here is the constructor that takes an initializer_list
myStringVector::myStringVector(std::initializer_list<myString> list){
this->reserve(list.size());
for(int i = 0; i < limit-1; i++){
construct(first+i, list.begin()[i]);
}
}
Here is the copy constructor
myStringVector::myStringVector(const myStringVector& data){
reserve((data.limit)-1);
for(int i = 0; i < data.limit-1; i++){
construct(&first+i, data.first+i);
}
}
Here is what reserve looks like
void myStringVector::reserve(int n){
this->first = allocate<myString>(n);
this->last = first+n;
this->limit = n+1;
}
My assertion in main is failing because I overloaded the == operator to first check if the limits are equal. If they are not, the function returns false. The function continues after that, but it is failing the assertion because I am getting my v1 to be 4, which is correct, and then my v2 is some large number that doesn't make sense.
I've tried rewriting my copy constructor to directly initialize the members of the class without using reserve and it still doesn't work. It is still assigning garbage values to limit. The rest of it is working fine.
Here is what main is outputting...
make it here?
abc
abc
4 7500072
Assertion failed: v1 == v2, file myCodeTres.cpp, line 58
I feel like I've tried everything I can think of to try and fix this but nothings working. Any advice for me?
Thanks.
Update: these are the compiler error I get when I leave the ampersand out.
In file included from myCodeTres.cpp:20:
myMemory.hpp: In instantiation of 'T* construct(T*, Args&& ...) [with T = myString; Args = {myString*}]':
myStringVector.cpp:25:36: required from here
myMemory.hpp:61:10: error: no matching function for call to 'myString::myString(myString*)'
61 | return new (p) T(std::forward(args)...);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**then there are some weird errors that are in blue on the compiler screen **
In file included from myCodeTres.cpp:23:
myString.cpp:42:1: note: candidate: 'myString::myString(myString&&)'
42 | myString::myString(myString&& move) : strLength(strlen(move.stringVar)) //move constructor to move contents from
| ^~~~~~~~
myString.cpp:42:31: note: no known conversion for argument 1 from 'myString*' to 'myString&&'
42 | myString::myString(myString&& move) : strLength(strlen(move.stringVar)) //move constructor to move contents from
| ~~~~~~~~~~~^~~~
myString.cpp:32:1: note: candidate: 'myString::myString(const myString&)'
32 | myString::myString(const myString& strToCpy) : strLength(strlen(strToCpy.stringVar)) //copy constructor that removes old elements
| ^~~~~~~~
myString.cpp:32:36: note: no known conversion for argument 1 from 'myString*' to 'const myString&'
32 | myString::myString(const myString& strToCpy) : strLength(strlen(strToCpy.stringVar)) //copy constructor that removes old elements
| ~~~~~~~~~~~~~~~~^~~~~~~~
In file included from myCodeTres.cpp:23:
myString.cpp:23:1: note: candidate: 'myString::myString(const char*, std::size_t)'
23 | myString::myString(const char* cPnt, std::size_t size) : strLength(size) //constructor to take a size and const ptr to char
| ^~~~~~~~
myString.cpp:23:1: note: candidate expects 2 arguments, 1 provided
myString.cpp:14:1: note: candidate: 'myString::myString(const char*)'
14 | myString::myString(const char* cPnt) : strLength(strlen(cPnt)) //constructor to initialize to a given const ptr to char.
| ^~~~~~~~
myString.cpp:14:32: note: no known conversion for argument 1 from 'myString*' to 'const char*'
14 | myString::myString(const char* cPnt) : strLength(strlen(cPnt)) //constructor to initialize to a given const ptr to char.
| ~~~~~~~~~~~~^~~~
myString.cpp:8:1: note: candidate: 'myString::myString()'
8 | myString::myString() //default constructor: initializes empty string.
| ^~~~~~~~
myString.cpp:8:1: note: candidate expects 0 arguments, 1 provided
update: whole class definition
#ifndef MYSTRINGVECTOR_HPP
#define MYSTRINGVECTOR_HPP
#include "myString.hpp"
#include <initializer_list>
class myStringVector{
private:
myString *first, *last;
int limit = 0;
public:
void reserve(int); //allocate memory at 'first' for n myString's
myStringVector(); //call to reserve(0)
myStringVector(std::initializer_list<myString>);
myStringVector(const myStringVector&);
bool empty();
bool operator==(const myStringVector&);
myString operator[](int);
int getSize() const;
};
#endif //MYSTRINGVECTOR_HPP_INCLUDED
.cpp file
#include "myStringVector.hpp"
#include "myMemory.hpp"
#include <initializer_list>
void myStringVector::reserve(int n){
this->first = allocate<myString>(n);
this->last = first+n;
this->limit = n+1;
}
myStringVector::myStringVector(){
this->reserve(0);
}
myStringVector::myStringVector(std::initializer_list<myString> list){
this->reserve(list.size());
for(int i = 0; i < limit-1; i++){
construct(first+i, list.begin()[i]);
}
}
myStringVector::myStringVector(const myStringVector& data){
reserve((data.limit)-1);
for(int i = 0; i < data.limit-1; i++){
construct(first+i, data.first+i);
}
}
bool myStringVector::empty(){
return (limit-1 == 0);
}
bool myStringVector::operator==(const myStringVector& data){
std::cout << limit << " " << data.limit << std::endl;
if(this->limit != data.limit)
return 0;
for(int i = 0; i < limit; i++){
if(*(first+i) != *(data.first+i))
return 0;
}
return 1;
}
myString myStringVector::operator[](int index){
return *(first+index);
}
int myStringVector::getSize() const{
return limit-1;
}
formatting is a bit off because uploading code to stackoverflow is tedious as can be.
Simple error, in your copy constructor
construct(&first+i, data.first+i);
should be
construct(first+i, data.first+i);
EDIT
This from the initializer list constructor works.
construct(first+i, list.begin()[i]);
Now think carefully about the types here. The first argument is simple enough, it's a pointer to myString i.e. myString*. The second is a little more complicated because you have to understand std::initializer_list but if you follow it through it's a const reference to myString, i.e. const myString&. Now this is fine because construct is defined like this
template <class T>
void construct(T* first, const T& second);
i.e. for some type T it's first argument is a pointer to that type, and it's second argument is a const reference to that type.
Now look at the types for the second use of construct (the one that doesn't compile)
construct(first+i, data.first+i);
The first argument is myString* and the second argument is const myString*. Two pointers! This is why it doesn't compile! Your solution was to add an extra pointer to the first argument. The types of
construct(&first+i, data.first+i);
are myString** and const myString*. That compiles because you have a double pointer on the left and a single pointer on the right but it means that you are constructing pointers to myString not myString objects, and that is not what you want.
What you should have done is remove the pointer from the second argument, not add an extra pointer to the first argument. In other words the correct code is this
construct(first+i, data.first[i]);
Now the types are myString* for the first and const myString& for the second argument. Exactly the same as in the other constructor.
Try that change and let me know how it goes.
The code does not compile. I do not understand what the error is, help please)
#include <iostream>
#include <fstream>
class Record{
std::string product_name;
std::string product_category;
int price;
int amount;
public:
Record(std::string newName, std::string newCategory, int newPrice, int newAmount){
product_name=newName;
product_category=newCategory;
price=newPrice;
amount=newAmount;
}
std::string getName(){
return product_name;
}
std::string getCategory(){
return product_category;
}
int getPrice(){
return price;
}
int getAmount(){
return amount;
}
void setName(std::string newName){
product_name=newName;
}
void setCategory(std::string newCategory){
product_category=newCategory;
}
void setPrice(int newPrice){
price=newPrice;
}
void setAmount(int newAmount){
amount=newAmount;
}
};
int main(){
Record r1;
r1.setName("beer");
r1.setCategory("alcohol");
r1.setPrice(12);
r1.setAmount(32);
Record r2("carrot", "vegetables", 123, 1932);
std::cout<<r1.getName()<<" "<<r1.getCategory()<<" "<<r1.getPrice()<<" "<<r1.getAmount()<< std::endl;
std::cout<<r2.getName()<<" "<<r2.getCategory()<<" "<<r2.getPrice()<<" "<<r2.getAmount()<< std::endl;
Record r3[2];
std::string a;
float b;
unsigned int c;
for(unsigned int i=0; i<2; ++i){
std::cout<<"input name: ";
std::cin>>a;
r3[i].setName(a);
std::cout<<"input category: ";
std::cin>>a;
r3[i].setCategory(a);
std::cout<<"input price: ";
std::cin>>b;
r3[i].setPrice(b);
std::cout<<"input amount: ";
std::cin>>c;
r3[i].setAmount(c);
}
for(unsigned int i=0; i<2; ++i){
std::cout<<r3[i].getName()<<" "<<r3[i].getCategory()<<" "<<r3[i].getPrice()<<" "<<r3[i].getAmount()<< std::endl;
}
return 0;
}
Error text:
g++ -Wall -c "main.cpp" ( /media/ad/4GB-NTFS/prog/laba2)
main.cpp: In function ‘int main()’:
main.cpp:46:12: error: no matching function for call to ‘Record::Record()’
Record r1;
^
main.cpp:12:1: note: candidate: Record::Record(std::__cxx11::string, std::__cxx11::string, int, int)
Record(std::string newName, std::string newCategory, int newPrice, int newAmount){
^
main.cpp:12:1: note: candidate expects 4 arguments, 0 provided
main.cpp:6:7: note: candidate: Record::Record(const Record&)
class Record{
^
main.cpp:6:7: note: candidate expects 1 argument, 0 provided
main.cpp:54:16: error: no matching function for call to ‘Record::Record()’
Record r3[2];
^
main.cpp:12:1: note: candidate: Record::Record(std::__cxx11::string, std::__cxx11::string, int, int)
Record(std::string newName, std::string newCategory, int newPrice, int newAmount){
^
main.cpp:12:1: note: candidate expects 4 arguments, 0 provided
main.cpp:6:7: note: candidate: Record::Record(const Record&)
class Record{
^
main.cpp:6:7: note: candidate expects 1 argument, 0 provided
Your class doesn't have a default constructor. So when you say:
Record r1;
the compiler doesn't know how to create the r1 object. You either need to provide all the parameters when r is created:
Record r1( "foo", "bar", 1, 2 );
or better completely rethink the design of your program.
You have overriden the constructor of your class, and so there is not one that accepts zero arguments as this requires:
Record r1;
Define a default constructor:
Record() {}
main.cpp:46:12: error: no matching function for call to ‘Record::Record()’
That is, at this point:
Record r1;
You are trying to instantiate the object r1 with a default constructor (Record::Record). Indeed you are not providing any parameter.
Moreover the compiler continues:
note: candidate expects 4 arguments, 0 provided
Following your class interface, the only way to instantiate a Record object is to use the only constructor provided, that is:
Record(std::string, std::string, int, int);
If you want to allow to instantiate an Record object with a default constructor you have to provide it.
C++11 allows you to write:
Record() = default;
In order to define a default constructor.
You have to define a constructor with no parameters.
Record r1 tries to call Record() but it doesn't find it. Just add the extra constructor in your class. It can be empty. This will also solve the same problem with Record r3[2].
P.S. (unrelated to the question, but helpful)
Looking at your code, I suggest you check out member initializer lists for implementing your constructors. Why? See here.
I have the following code:
#include <iostream>
#include <string>
using namespace std;
string combine(string a, string b, string c);
int main() {
char name[10] = {'J','O','H','N','\0'};
string age = "24";
string location = "United Kingdom";
cout << combine(name,age,location);
return 0;
}
string combine(string a, string b, string c) {
return a + b + c;
}
This compiles fine with no warnings or errors despite the combine function expecting a string and receiving a char array, is this because a string is stored as a char array?
Why does C++ allow a char array as an argument when it's expecting a string?
Because std::string has such a conversion constructor, which supports implicit conversion of char const* into std::string object.
This is the constructor which is responsible for this conversion:
basic_string( const CharT* s, const Allocator& alloc = Allocator());
Have a look at the documentation and other constructors.
It's because there is an automatic conversion from a char array to a string.
string has a constructor like this (simplified)
class string
{
public:
string(const char* s);
...
};
This constructor can be called automatically, so your code is equivalent to this
cout << combine(string(name),age,location);