I need to be able to initialize a const member inside the constructor, which counts up every time I create a new object. I was shown in school how its suppose to work, but I'm getting errors all the time. It's something to do with the copy constructor.
Here's the code and the compiler errors:
class kunde {
public:
kunde(string name, int alter);
kunde(const kunde& orig);
~kunde();
int GetAlter() const;
string GetName() const;
const int GetKnr() const;
private:
string name;
int alter;
const int knr;
static int cnt;
static int MaxKnr;
};
int kunde::cnt = 0;
int kunde::MaxKnr = 1000;
kunde::kunde(string name, int alter):knr(MaxKnr++) {
this->name = name;
this->alter = alter;
}
kunde::kunde(const kunde& orig):knr(MaxKnr++){
this->name = orig.name;
this->alter = orig.alter;
}
kunde::~kunde() {
}
int kunde::GetAlter() const {
return alter;
}
string kunde::GetName() const {
return name;
}
const int kunde::GetKnr() const {
return knr;
}
main.cpp: In function 'int main(int, char**)':
main.cpp:35:15: error: use of deleted function 'kunde& kunde::operator=(const kunde&)'
v[0] = v[1];
^
In file included from main.cpp:17:0:
kunde.h:19:7: note: 'kunde& kunde::operator=(const kunde&)' is implicitly deleted because the default definition would be ill-formed:
class kunde {
^~~~~
kunde.h:19:7: error: non-static const member 'const int kunde::knr', can't use default assignment operator
knr is suppose to be an account number. each time you create an object it creates a new const account number which stays.
As stated in comments, since knr is const, the compiler cannot generate a default copy-assignment operator= for the class, which is exactly what the compiler is complaining about for the v[0] = v[1]; statement:
note: 'kunde& kunde::operator=(const kunde&)' is implicitly deleted because the default definition would be ill-formed
const members cannot be re-assigned once initialized, thus cannot be copied.
The elements in a vector must be CopyAssignable and CopyConstructible (at least until C++11), but your class does not have a viable copy-assignment operator=, so it is not CopyAssignable (and it is not MoveAssignable in C++11 either, since a viable move-assignment operator= can't be generated, either).
The solution is to implement a copy-assignment operator= (and optionally a move-assignment operator=) that ignores knr, eg:
class kunde {
public:
kunde(string name, int alter);
kunde(const kunde& orig);
kunde(kunde&& orig);
...
kunde& operator=(const kunde& rhs);
kunde& operator=(kunde&& rhs);
...
private:
string name;
int alter;
const int knr;
...
};
kunde::kunde(string name, int alter)
: knr(MaxKnr++), name(name), alter(alter)
{
}
kunde::kunde(const kunde& orig)
: knr(MaxKnr++), name(orig.name), alter(orig.alter)
{
}
kunde::kunde(kunde&& orig)
: knr(MaxKnr++), name(std::move(orig.name)), alter(orig.alter)
{
}
kunde& kunde::operator=(const kunde& rhs)
{
if (&rhs != this)
{
name = rhs.name;
alter = rhs.alter;
// CAN'T BE DONE, SO IGNORE IT
// knr = rhs.knr;
}
return *this;
}
kunde& kunde::operator=(kunde&& rhs)
{
name = std::move(rhs.name);
alter = rhs.alter;
// CAN'T BE DONE, SO IGNORE IT
// knr = rhs.knr;
return *this;
}
Related
I am new to the topic of overloading copy constructors and I just wanted someone to look at my code for my class and see if I am overloading my copy constructor correctly. It is only using a single string as user input. Also, do I need the '&' or not?
class TODO {
private:
string entry;
public:
List* listArray = nullptr;
int itemCount = 0, currInvItem = 0;
int maxLength = 22;
TODO() { entry = ""; };
TODO(const string& ent) { setEntry(ent); }; // Is this correct?
void setEntry(string ent) { entry = ent; };
string getEntry() const { return entry; };
void greeting();
void programMenu();
void newArray();
void getList();
void incList();
void delTask();
string timeID();
string SystemDate();
friend istream& operator >>(istream& in, TODO& inv);
friend ostream& operator <<(ostream& out, TODO& inv);
void componentTest();
void setTask(string a);
string getTask();
bool validTask(string a);
bool notEmpty(string e);
};
That's correct, but it's just a constructor of TODO taking a const reference to a string. You can test it here.
Passing const string& ent (by const reference) is not a bad idea. Another option would be to pass string ent (by value), and move that string when initializing entry, i.e. : entry{ std::move(ent) }. As here.
The class TODO has a default copy constructor. Check the line at the Insight window here (you'll have to click Play first).:
// inline TODO(const TODO &) noexcept(false) = default;
The default copy constructor would just copy the members, including the List pointer, but not the List elements (shallow copy). Notice both instances of TODO would be pointing to the same List elements, and any operation on them would affect both instances.
Should you want to implement a custom copy constructor, the syntax would be (see here):
TODO(const TODO& other) {
std::cout << "custom copy_ctor\n";
*this = other;
// Copy listArray elements
...
}
Why I've getting that error? I saw a video running such code and has no error at all. I don't would like to define outside of class. What is wrong?
class Person
{
public:
int age;
string name;
bool operator < (const Person& rhs) { return age < rhs.age; }
};
int main()
{
std::set<Person> my;
Person p{ 10, "Eduardo" };
my.insert(p);
}
You need to mark your operator< as being const so that it can be called on objects of type const Person:
bool operator < (const Person& rhs) const { return age < rhs.age; }
^^^^^
The default Compare template parameter of std::set<Person> is std::less<Person>, which takes const Person& parameters in its operator().
Your operator should be const anyway since it does not modify the members of the Person object it is called on.
I'm designing a class that ought to have a const data member called K. I also want this class to have a copy assignment operator, but the compiler seems to implicitly delete the copy assignment operator from any class with const data members. This code illustrates the essential problem:
class A
{
private:
const int K;
public:
A(int k) : K(k) {} // constructor
A() = delete; // delete default constructor, since we have to set K at initialization
A & operator=(A const & in) { K = in.K; } // copy assignment operator that generates the error below
}
Here's the error it generates:
constructor.cpp:13:35: error: cannot assign to non-static data member 'K' with const-
qualified type 'const int'
A & operator=(A const & in) { K = in.K; }
~ ^
constructor.cpp:6:13: note: non-static data member 'K' declared const here
const int K;
~~~~~~~~~~^
1 error generated.
I think I understand why the compiler does this; the instance of the class I'd want to copy to has to exist before it can be copied to, and I can't assign to K in that target instance if it's const, as I'm trying to do above.
Is my understanding of this problem correct? And if so, is there a way around this problem? That is, can I define a copy constructor for my class and still give K const-like protection?
In C++, a class with a const data member may have a copy constructor.
#include <iostream>
class A
{
private:
const int k_;
public:
A(int k) : k_(k) {}
A() = delete;
A(const A& other) : k_(other.k_) {}
int get_k() const { return k_; }
};
int main(int argc, char** argv)
{
A a1(5);
A a2(a1);
std::cout << "a1.k_ = " << a1.get_k() << "\n";
std::cout << "a2.k_ = " << a2.get_k() << "\n";
}
Output:
a1.k_ = 5
a2.k_ = 5
In C++, a class with a const data member may not use the default assignment operator.
class A
{
private:
const int k_;
public:
A(int k) : k_(k) {}
A() = delete;
A(const A& other) : k_(other.k_) {}
int get_k() const { return k_; }
};
int main(int argc, char** argv)
{
A a1(5);
A a2(0);
a2 = a1;
}
Yields a compile time error:
const_copy_constructor.cpp: In function ‘int main(int, char**)’:
const_copy_constructor.cpp:18:10: error: use of deleted function ‘A& A::operator=(const A&)’
18 | a2 = a1;
| ^~
const_copy_constructor.cpp:1:7: note: ‘A& A::operator=(const A&)’ is implicitly deleted because the default definition would be ill-formed:
1 | class A
| ^
const_copy_constructor.cpp:1:7: error: non-static const member ‘const int A::k_’, can’t use default assignment operator
In C++, a class with a const data member may use a non-default assignment operator as long as you don't attempt to change the const data member, but you better think long and hard about what it means to use this assignment operator if one of the underlying members cannot be modified.
class A
{
private:
const int k_;
public:
A(int k) : k_(k) {}
A() = delete;
A(const A& other) : k_(other.k_) {}
A& operator=(A const& other)
{
// do nothing
return *this;
}
int get_k() const { return k_; }
};
int main(int argc, char** argv)
{
A a1(5);
A a2(0);
a2 = a1;
}
Yields no compile time errors.
As of c++20, you can now copy objects that have one or more const member objects by defining your own copy-assignment operator.
class A
{
private:
const int K;
public:
A(int k) : K(k) {} // constructor
A() = delete; // delete default constructor, since we have to set K at initialization
// valid copy assignment operator in >= c++20
A& operator=(A const& in) {
if (this != &in)
{
std::destroy_at(this);
std::construct_at(this, in);
}
return *this;
}
};
This was made possible by changes in basic.life which allows transparent replacement of objects, including those containing const sub-objects, w/o UB.
I need to have set as a class member variable, but also need it's comparision function object use the attributes of the class.
class Example
{
int _member1;
set<string, MyCmp> _myNameSet;
class MyCmp
{
Example& myEx;
MyCmp( const Example& ex) {
myEx = ex;
}
bool operator() (const string& lhs, const string& rhs)
{
/// Use "_member1" here ...
myEx._member1;
/// Do something ....
}
}
};
So here my question is, how do i pass the Example object as an argument to the MyCmp constructor? Since the "_myNameSet" is an member variable.
If it was not an member variable, there is a way i know:
void Example::functionBlah()
{
MyCmp obj(&(*this));
set<String, MyCmp> myLocalSet(obj);
}
You may use initializer list in constructor:
class Example
{
public:
Example() : _member1(0), _myNameSet(this) {}
Example(const Example&) = delete;
Example& operator = (const Example&) = delete;
// Other stuff
private:
int _member1;
set<string, MyCmp> _myNameSet;
};
I have the following code:
class B {
public:
B(const std::string& str):m_str(str) { }
B(const B& b):m_str(b.m_str) { }
B& operator=(const B& b) { m_str = b.m_str; return *this; }
private:
std::string m_str;
};
main()
{
std::string a = "abc";
B b(a);
}
class B belongs to the client. I can't change it and I may not even know its specific name ("B" is simply an example); all I know is there's a client class that accepts a std::string for its constructor. Now I want to change the type of "a" in main() from std::string to A, as defined below:
class A {
public:
A(const std::string& str):m_str(str) { }
A(const char *str):m_str(str) { }
A(const A& a):m_str(a.m_str) { }
A& operator=(const A& a) { m_str = a.m_str; return *this; }
private:
std::string m_str;
};
So now I have a new main():
main()
{
A a = "abc";
B b(a);
}
This can't compile properly as it is. Is there anything I can do without changing the new main()? I can't change class B, and class A should not reference class B in any way.
Thanks!
add an cast operator
class A {
public:
....
operator const std::string()
{
return m_str;
}
Add user-defined conversion function as:
class A
{
public:
//other code
operator std::string()
{
return m_str;
}
//...
};
This allows you write these:
B b(a); //which is what you want
std::string s = a; //this is also okay;
void f(std::string s) {}
f(a); //a converts into std::string, by calling user-defined conversion function
However you cannot write this:
const A ca("hi");
std::string s = ca; //error
Its because ca is a const object which cannot invoke non-const user-defined conversion function.
Note the user-defined conversion function returns the string by value which means it returns a copy of the original string. So you may want to avoid this by defining it as:
operator const std::string &() const
{
return m_str;
}
Now, with this you can write this:
const A ca("hi");
std::string s = ca; //ok
A a("hi");
std::string s = a; //ok
How about
int main() //note the correct declaration of main
{
A a = "abc";
B b(a.GetString()); //GetString returns m_str;
}
By the way, all your definitions of copy constructors and copy-assignment operators coincide with the definitions the compiler would have auto-generated for you.
Edit
I just noticed your constraint that main() should not change. In this case you can define a conversion function from A to string (but it can be dangerous, note).
class A
{
...
operator std::string() const {return m_str; }
};
Add this to class A:
operator std::string () { return m_str; }