if I have a class with attributes like this:
struct MyClass {
double **arowofpointers;
int capacity;
};
Now, if the task says "make sure this line of code in the main function is legal:
MyClass a(10); //makes a variable whose type is MyClass that has the capacity of 10
But make sure that the following line of code in the main function is not legal:
MyClass a=10;
Still, the following line of your code in the main function should be legal:
a=b+c;
where, a,b and c are all variables whose type is MyClass.
Which constructors should I make? Is there anything I should set on delete or something?
Constructing an instance of type MyClass like this
MyClass a(10);
requires a constructor that takes an integer parameter:
class MyClass {
public:
MyClass(int param);
// ...
};
But as constructors are implicit by default (which is unfortunate), allowing for MyClass a = 10;, you need to make it explicit:
// This constructor must be called explicitly via MyClass(int)
explicit MyClass(int param);
This will make the compiler complain when it encounters MyClass a = 10;.
For the operator part of your question, you might want to have a look at this (the "Arithmetic Operators" part).
MyClass a(10); requires a conversion constructor that takes an integer as input. To prevent MyClass a=10;, make this constructor explicit.
a = b + c; requires an operator+ to concatenate two MyClass objects, and an operator= to assign one MyClass object to another. If you want to support initializations like MyClass a = b;, MyClass a = b + c;, etc, you will also need a copy constructor as well.
And don't forget a destructor.
So, you will need these constructors and operators in your struct:
struct MyClass
{
private:
double **arowofpointers;
int capacity;
public:
// default constructor
MyClass();
// conversion constructor
explicit MyClass(int cap);
// copy constructor
MyClass(const MyClass &src);
// move constructor (C++11 and later only, optional but recommended)
MyClass(MyClass &&src);
// destructor
~MyClass();
// copy assignment operator
MyClass& operator=(const MyClass &rhs);
// move assignment operator(C++11 and later only, optional but recommended)
MyClass& operator=(MyClass &&rhs);
// concatenation operator overload
MyClass operator+(const MyClass &rhs) const;
// compound concatenation assignment operator (optional)
MyClass& operator+=(const MyClass &rhs);
// swap helper
void swap(MyClass &other);
};
// std::swap() overload
void swap(MyClass &lhs, MyClass &rhs);
Where the implementations might look something like this:
#include <algorithm>
MyClass::MyClass()
: arowofpointers(nullptr), capacity(0)
{
}
MyClass::MyClass(int cap)
: arowofpointers(new double*[cap]), capacity(cap)
{
std::fill_n(arowofpointers, capacity, nullptr);
}
MyClass::MyClass(const MyClass &src)
: arowofpointers(new double*[src.capacity]), capacity(src.capacity)
{
std::copy(src.arowofpointers, src.arowofpointers + capacity, arowofpointers);
}
MyClass::MyClass(MyClass &&src)
: arowofpointers(nullptr), capacity(0)
{
src.swap(*this);
}
MyClass::~MyClass()
{
delete[] arowofpointers;
}
MyClass& MyClass::operator=(const MyClass &rhs)
{
if (&rhs != this)
MyClass(rhs).swap(*this);
return *this;
}
MyClass& MyClass::operator=(MyClass &&rhs)
{
MyClass tmp(std::move(*this));
rhs.swap(*this);
return *this;
}
MyClass MyClass::operator+(const MyClass &rhs) const
{
MyClass tmp(capacity + rhs.capacity);
std::copy(arowofpointers, arowofpointers + capacity, tmp.arowofpointers);
std::copy(rhs.arowofpointers, rhs.arowofpointers + rhs.capacity, tmp.arowofpointers + capacity);
return tmp;
}
MyClass& MyClass::operator+=(const MyClass &rhs)
{
MyClass tmp = *this + rhs;
tmp.swap(*this);
return *this;
}
void swap(MyClass &lhs, MyClass &rhs)
{
lhs.swap(rhs);
}
That being said, if you use std::vector instead, then you don't need to handle most of this yourself, let the compiler and STL to the heavy work for you:
#include <vector>
struct MyClass
{
private:
std::vector<double*> arowofpointers;
public:
MyClass();
explicit MyClass(int cap);
MyClass operator+(const MyClass &rhs) const;
MyClass& operator+=(const MyClass &rhs);
void swap(MyClass &other);
};
void swap(MyClass &lhs, MyClass &rhs);
#include <algorithm>
MyClass::MyClass()
: arowofpointers()
{
}
MyClass::MyClass(int cap)
: arowofpointers(cap, nullptr)
{
}
MyClass MyClass::operator+(const MyClass &rhs) const
{
MyClass tmp(arowofpointers.capacity() + rhs.arowofpointers.capacity());
tmp.arowofpointers.insert(tmp.arowofpointers.end(), arowofpointers.begin(), arowofpointers.end();
tmp.arowofpointers.insert(tmp.arowofpointers.end(), rhs.arowofpointers.begin(), rhs.arowofpointers.end();
return tmp;
}
MyClass& MyClass::operator+=(const MyClass &rhs)
{
MyClass tmp = *this + rhs;
tmp.swap(*this);
return *this;
}
void swap(MyClass &lhs, MyClass &rhs)
{
lhs.swap(rhs);
}
Related
I'm having trouble creating a QVariant with a custom type. Here is a small example that shows what I want to achieve:
main.cpp:
#include "MyClass.h"
int main() {
MyClass some_object;
QVariant variant(some_object);
return 0;
}
include/MyClass.h:
#pragma once
#include <QtCore>
class MyClass {
public:
MyClass() : _my_member(0.0) {}
MyClass(const MyClass &other) { _my_member = other._my_member; }
~MyClass() {}
MyClass& operator=(MyClass& other)
{
swap(*this, other);
return *this;
}
MyClass(MyClass&& other) : MyClass()
{
swap(*this, other);
}
friend void swap(MyClass& first, MyClass& second)
{
using std::swap;
swap(first._my_member, second._my_member);
}
private:
float _my_member;
};
Q_DECLARE_METATYPE(MyClass);
The build fails with the following error:
error: no matching function for call to ‘QVariant::QVariant(MyClass&)’
QVariant variant(some_object);
^
How can I solve this error?
You're calling QVariant's constructor with an instance of MyClass: there is no such constructor in QVariant. What you're looking for is QVariant::fromValue:
#include "MyClass.h"
int main() {
MyClass some_object;
QVariant variant = QVariant::fromValue(some_object);
return 0;
}
Alternatively, you can use QVariant::setValue:
#include "MyClass.h"
int main() {
MyClass some_object;
QVariant variant;
variant.setValue(some_object);
return 0;
}
Note:
To retrieve the value from QVariant, you will have to use the template method QVariant::value, optionally with QVariant::canConvert:
if (variant.canConvert<MyClass>())
MyClass retrieve_object = variant.value<MyClass>();
Note2:
Looks like you tried to implement copy-and-swap idiom. Your operator= should be:
MyClass& operator=(MyClass other)
{
swap(*this, other);
return *this;
}
Instead of:
MyClass& operator=(MyClass& other)
{
swap(*this, other);
return *this;
}
If you perform a swap with a reference, other will be modified, and I doubt you want it.
To make QVariant object from your class use fromValue method of QVariant:
QVariant variant = QVariant::fromValue(some_object);
You have error, because there is no such constructor in QVariant with parameter of type MyClass&.
I would like to leave the two instances unchanged, and return a new one.
Currently I am doing it this way:
class myClass {
public:
myClass operator +(const myClass &obj) {
myClass ret = *this;
// some operation
return ret;
}
// functions...
};
It works, but I am not sure, if it is the correct way
Edit
The operator + is just an example. I'm just curious, how the immutable functions/methods should be written in C++
If myClass is supposed to be immutable under addition, you probably want to make operator+ a free function rather than a class member. (You might have to make it a friend function.)
myClass operator+(const myClass &lhs, const myClass &rhs) {
return myClass( /* some operation */ );
}
Note that both operands are taken by const reference, so you know you cannot accidentally change them (maintaining the immutability property). You're returning a new instance of myClass, which is now immutable. You construct and return the result in one step, because, if myClass really is immutable, you might not be able to default construct one and then set its value.
Here's a stupid example:
class myClass {
public:
explicit myClass(int x) : m_x(x) {}
friend myClass operator+(const myClass &lhs, const myClass &rhs);
private:
int m_x;
};
myClass operator+(const myClass &lhs, const myClass &rhs) {
return myClass(lhs.m_x + rhs.m_x);
}
If you really want to implement it as a class method, the method should be marked const to ensure the implementation doesn't accidentally mutate the left-hand instance.
Binary arithmetic operators (like operator+) are often defined in terms of the arithmetic self-assignment operators (like operator+=), which are obviously not immutable. If we add this method to myClass:
myClass &operator+=(const myClass &rhs) {
m_x += rhs.m_x;
return *this;
}
Then the common idiom for defining operator+ would be:
myClass operator+(const myClass &lhs, const myClass &rhs) {
myClass result = lhs;
result += rhs;
return result;
}
Now the implementation of operator+ doesn't require any of the private members of the class, so it no longer needs to be declared as a friend function.
We have the following:
(pseudoish)
class MyClass
{
private:
struct MyStruct{
MyStruct operator=(const MyOtherStruct& rhs);
int am1;
int am2;
};
};
We'd like to overload the = operator in the MyClass.cpp to do something like:
MyStruct&
MyStruct::operator=(const MyOtherStruct& rhs)
{
am1 = rhs.am1;
am2 = rhs.am2;
}
However, it doesn't want to compile. We're getting an error similar to
"missing ; before &"
and
"MyStruct must be a class or namespace if followed by ::"
Is there some concept here I'm missing?
You need to move your operator= for MyStruct into the struct declaration body:
class MyClass
{
private:
struct MyStruct{
int am1;
int am2;
MyStruct& operator=(const MyOtherStruct& rhs)
{
am1 = rhs.am1;
am2 = rhs.am2;
return *this;
}
};
};
Or if that's not possible because MyOtherStruct is incomplete or don't want to clutter the class declaration:
class MyClass
{
private:
struct MyStruct{
int am1;
int am2;
MyStruct& operator=(const MyOtherStruct& rhs);
};
};
inline MyClass::MyStruct& MyClass::MyStruct::operator=(const MyOtherStruct& rhs)
{
am1 = rhs.am1;
am2 = rhs.am2;
return *this;
}
The syntax is
MyStruct& operator=(const MyOtherStruct& rhs) {
// assignment logic goes here
return *this;
}
for an operator directly within the body of MyStruct. Also note that I added the idiomatic return *this to let the assignment return a reference to this object.
EDIT in response to OP editing the question.
You can also declare the operator in the body, and define it somewhere else. In this case, the syntax is:
MyClass::MyStruct& MyClass::MyStruct::operator=(const MyOtherStruct& rhs) {
// assignment logic goes here
return *this;
}
I'm wondering if there is a way to implement copy constructors and assignment operators such that only a small modification is needed when these are redefined for a class.
For example, consider a class as such:
class Foo {
private:
int* mInt_ptr;
/* many other member variables
of different types that aren't
pointers */
public:
Foo();
Foo(const Foo&);
Foo& operator=(const Foo&);
~Foo();
};
Now, in order to deal with the pointer mInt_ptr I would need to handle it appropriately in the copy constructor and assignment operator. However, the rest of the member variables are safe to do a shallow copy of. Is there a way to do this automatically?
Once a class becomes large it may become tedious and unwieldy to explicitly write out the operations to copy the non-pointer member variables, so I'm wondering if there is a way to write, say, a copy constructor such as:
Foo::Foo(const Foo& tocopy)
{
mInt_ptr = new int(*tocopy.mInt_ptr);
/* Do shallow copy here somehow? */
}
rather than the explicit form of:
Foo::Foo(const Foo& tocopy)
{
mInt_ptr = new int(*tocopy.mInt_ptr);
mVar1 = tocopy.mVar1;
mVar2 = tocopy.mVar2;
...
...
mVarN = tocopy.mVarN;
}
Generally, don't use raw pointers, for exactly the reason that you're now fighting with. Instead, use a suitable smart pointer, and use copy-swap assignment:
class Foo
{
int a;
Zip z;
std::string name;
value_ptr<Bar> p;
public:
Foo(Foo const &) = default;
Foo & operator=(Foo rhs)
{
rhs.swap(*this);
return *this;
}
void swap(Foo & rhs)
{
using std::swap;
swap(a, rhs.a);
swap(z, rhs.z);
swap(name, rhs.name);
swap(p, rhs.p);
}
};
namespace std { template <> void swap<Foo>(Foo & a, Foo & b) { a.swap(b); } }
The value_ptr could be a full-blown implementation, or something simple such as this:
template <typename T> // suitable for small children,
class value_ptr // but not polymorphic base classes.
{
T * ptr;
public:
constexpr value_ptr() : ptr(nullptr) { }
value_ptr(T * p) noexcept : ptr(p) { }
value_ptr(value_ptr const & rhs) : ptr(::new T(*rhs.ptr)) { }
~value_ptr() { delete ptr; }
value_ptr & operator=(value_ptr rhs) { rhs.swap(*this); return *this; }
void swap(value_ptr & rhs) { std::swap(ptr, rhs.ptr); }
T & operator*() { return *ptr; }
T * operator->() { return ptr; }
};
How about you wrap all the shallow-copy bits in a small helper struct and use the default copy behaviour there.
class Foo {
private:
int* mInt_ptr;
struct helper_t
/* many other member variables
of different types that aren't
pointers */
} mHelper;
public:
Foo();
Foo(const Foo&);
Foo& operator=(const Foo&);
~Foo();
};
Foo::Foo(const Foo& tocopy)
{
mInt_ptr = new int(*tocopy.mInt_ptr);
mHelper = tocopy.mHelper;
}
Using better primitives, as Kerrek suggested, seems like better design though. This is just another possibility.
Regardless if you use raw pointers or smart pointers the Kerrek's solution is right in the sense that you should make a copy constructor, destructor and swap and implement assignment using those:
class Foo
{
private:
int* mInt_ptr;
// many other member variables
// of different types
public:
Foo()
: mInt_ptr(NULL)
// initialize all other members
{}
Foo(const Foo& that)
: mInt_ptr(new int(*that.mInt_ptr) )
// copy-construct all other members
{}
Foo& operator=(const Foo& that)
{
// you may check if(this == &that) here
Foo(that).swap(*this);
return *this;
}
~Foo()
{
delete mInt_ptr;
// and release other resources
}
void swap(Foo& that)
{
std::swap(mInt_ptr, that.mInt_ptr);
// swap all members
}
};
The members are inline here just to keep it compact, usually it is not advisable to burden class definition with inline member definitions.
I want to have an object that contains a reference, and put that object into a vector...
Must I use smart pointers instead of a member references in any object I want to push into a vector? This was what I wanted to do:
#include <string>
#include <vector>
using namespace std;
class MyClass {
public:
MyClass(const string& str_ref); //constructor
MyClass(const MyClass& mc); //copy constructor
private:
string& my_str;
};
MyClass::MyClass(const string& str_ref) :
my_str(str_ref)
{}
MyClass::MyClass(const MyClass& mc) :
my_str(mc.my_str)
{}
int main() {
//create obj and pass in reference
string s = "hello";
MyClass my_cls(s);
//put into vector
vector<MyClass> vec;
vec.push_back(my_cls);
return 0;
}
//Throws Error
//ref.cpp:6:7: error: non-static reference member ‘std::string& MyClass::my_str’, can’t use default assignment operator
However it says I need to implement my own operator=() as the default generated one isn't valid but of course, there is no legal way to do so...
#include <string>
#include <vector>
using namespace std;
class MyClass {
public:
MyClass(const string& str_ref); //constructor
MyClass(const MyClass& mc); //copy constructor
MyClass operator=(const MyClass& mc); //operator =
private:
string& my_str;
};
MyClass::MyClass(const string& str_ref) :
my_str(str_ref)
{}
MyClass::MyClass(const MyClass& mc) :
my_str(mc.my_str)
{}
//not a constructor. should not construct new object
//and return that?
MyClass MyClass::operator=(const MyClass& mc) {
if (this != &mc) { //test for self-assignment.
my_str(mc.my_str); //can't reseat refs. this shouldn't work.
}
return *this;
}
int main() {
//create obj and pass in reference
string s = "hello";
MyClass my_cls(s);
//put into vector
vector<MyClass> vec;
vec.push_back(my_cls);
return 0;
}
//THROWS:
//ref2.cpp: In constructor ‘MyClass::MyClass(const string&)’:
//ref2.cpp:18:19: error: invalid initialization of reference of type ‘std::string& {aka //std::basic_string<char>&}’ from expression of type ‘const string {aka const //std::basic_string<char>}’
//ref2.cpp: In member function ‘MyClass MyClass::operator=(const MyClass&)’:
//ref2.cpp:29:18: error: no match for call to ‘(std::string {aka std::basic_string<char>}) //(std::string&)’
So am I forced to use a smart pointer here or anything other than a reference?
EDIT: This is a simplification. String& is not the object being passed, it's a more complex object itself containing a vector object.
You can store a raw pointer instead of a reference here. Raw pointers can be reseated, and so they're a good way to emulate reseatable references in C++.
class MyClass
{
public:
MyClass(const string& str_ref);
MyClass(const MyClass& mc);
// by the way, operator= should return a reference
MyClass& operator=(const MyClass& mc);
private:
string* my_str;
};
This way, operator= will be a cinch to implement.
How about using std::reference_wrapper<T>? Now you're not forced to refactor your code to allow a smart pointer, but you're also not using an internal pointer that someone may come along later and think they're supposed to delete.
class MyClass
{
public:
MyClass(string &str_ref)
: my_str(std::ref(str_ref))
{
}
private:
std::reference_wrapper<std::string> my_str;
};
One caveat. Please be sure to check for self-assignment:
MyClass& MyClass::operator=(MyClass const& from) {
if (this != &from) {
this->~MyClass();
new(this) MyClass(from);
}
return *this;
}
If you want to be able to still use the assignment operator of your member (i.e. std::string& operator=(std::string const&)) you cannot use the otherwise excellent suggestion of std::reference_wrapper. But you can rebuild your object from scratch using the copy constructor so your member may actually be a raw reference:
MyClass& MyClass::operator=(MyClass const& from) {
this->~MyClass();
new(this) MyClass(from);
return *this;
}