This question already has an answer here:
Is there a way to do constructor delegation using a conditional in C++11?
(1 answer)
Closed 4 years ago.
The typical way (and the only one widely used) to delegate some part of the object initialization to another constructor is to call it using the initializer list, like so:
class Window
{
Window(int a);
Window(int a, void *b);
Window(string a);
Window(double a, double b) : Window((int) (a + b))
{
}
};
But sometimes there is a necessity to do some prep work or branch constructor calls.
Is this how it should be done?
class Window
{
Window(int a);
Window(int a, void *b);
Window(string a);
Window(double a, double b)
{
if (a * b == 0.0)
{
Window("ZERO");
}
else if (a * b > 100.0)
{
Window((int) (a + b), x);
}
else
{
Window((int) (a + b));
}
}
};
I have never seen anything like this; this is just my guess of how it could look (which appears to be working).
Are there any side effects or any undefined behavior to calling other constructors from the constructor body (and not from the initializer list)?
#Edit: I'm including the code I'm having a problem with below. Basically, I have an object that is a container for some data, and I store it in a std::map<CString, ValueContainer>. I employ a copy constructor, and an overloaded assignment operator. The insertion works fine when I add an instance to the map using the [] operator, but it doesn't when I use the insert_or_assign method, because it uses the copy constructor (which uses the branching that is the problem here). Here's a simplification of the class.
class ValueContainer
{
ValueContainer(const VOID *p_data, ULONG p_size)
{
if (p_data != NULL)
{
if (p_size > 0)
{
if (p_size <= sizeof(shortData))
{
memcpy(shortData, p_data, p_size);
}
else
{
longData = new BYTE[p_size];
memcpy(longData, p_data, p_size);
}
}
hasValue = TRUE;
size = p_size;
}
else
{
hasValue = FALSE;
size = 0;
}
}
ValueContainer(const ValueContainer &p_value)
{
if (p_value.HasValue())
{
if (p_value.size <= sizeof(shortData))
{
ValueContainer(p_value.shortData, p_value.size);
}
else
{
ValueContainer(p_value.longData, p_value.size);
}
}
else
{
ValueContainer();
}
}
ValueContainer(VOID) : ValueContainer(NULL, 0)
{
}
ValueContainer &operator =(const ValueContainer &p_value);
{
if (p_value.hasValue)
{
if (p_value.size <= sizeof(shortData))
{
if (longData != NULL)
{
free(longData);
longData = NULL;
}
memcpy(shortData, p_value.shortData, p_value.size);
}
else
{
if (p_value.size > size)
{
longData = (BYTE *) realloc(longData, p_value.size);
}
memcpy(longData, p_value.longData, p_value.size);
}
hasValue = TRUE;
size = p_value.size;
}
else
{
if (longData != NULL)
{
free(longData);
longData = NULL;
}
hasValue = FALSE;
size = 0;
}
return *this;
}
private:
BYTE shortData[16];
BYTE *longData = NULL;
BOOL hasValue;
ULONG size;
}
For your first example, this might be the way it can be done:
class Window
{
private:
void constructor_method(int a);
void constructor_method(int a, void *b);
void constructor_method(string a);
public:
Window(int a) {
constructor_method(a);
}
Window(int a, void *b) {
constructor_method(a, b);
}
Window(string a) {
constructor_method(a);
}
Window(double a, double b)
{
if (a * b == 0.0)
{
constructor_method("ZERO");
}
else if (a * b > 100.0)
{
constructor_method((int) (a + b), x);
}
else
{
constructor_method((int) (a + b));
}
}
};
I think the short answer to this question is "simplify your code." But if that's not an option, I think the next best alternative is a factory method:
class Window
{
private:
Window(int a);
Window(int a, void *b);
Window(string a);
public:
static Window Create(double a, double b)
{
if (a * b == 0.0)
{
return Window("ZERO");
}
else if (a * b > 100.0)
{
return Window((int) (a + b), x);
}
else
{
return Window((int) (a + b));
}
}
};
Related
class
{
public:
void func(const int val, const bool flag)
{
if(flag)
{
while(!lower.empty() && val <= lower.top())
{
// do a bunch of stuff with lower
}
}
else
{
while(!higher.empty() && val >= higher.top())
{
// do a bunch of stuff with higher, but it's the same stuff as would've done
// for lower
}
}
}
private:
std::stack<int> lower;
std::stack<int> higher;
}
I'm trying to figure out a better way to write the clauses because currently, I have a lot of duplicate code in both. The only difference is one clause operates on lower and the other on higher and the <= in the first clause is changed to >= higher in the second one.
I could wrap the clause in a helper function and call it in each clause (and pass in the lower and higher as an argument), e.g.,
class
{
public:
void func(const int val, const bool flag)
{
if(flag)
{
helper(lower, comparer);
}
else
{
helper(lower, comparer);
}
}
void helper(std::stack<int> &st)
{
// do a bunch of stuff with st
}
private:
std::stack<int> lower;
std::stack<int> higher;
}
I'm not sure if this is a good idea and if it is, I'm not sure how to get around the >= vs. <=. I'm hoping for suggestions on my design!
You can do something like the following:
class
{
public:
void func(const int val, const bool flag)
{
std::stack<int> *st;
bool (*compare)(int, int);
if (flag)
{
st = &lower;
compare = [](int a, int b){ return a <= b; };
}
else
{
st = &higher;
compare = [](int a, int b){ return a >= b; };
}
while (!st->empty() && compare(val, st->top()))
{
// do a bunch of stuff with *st
}
}
private:
std::stack<int> lower;
std::stack<int> higher;
}
Alternatively, using a helper would certainly work, too:
class
{
public:
void func(const int val, const bool flag)
{
if (flag)
func_helper(lower, val, std::less_equal{});
else
func_helper(higher, val, std::greater_equal{});
}
private:
std::stack<int> lower;
std::stack<int> higher;
template<typename Comparer>
void func_helper(stack<int> &st, const int val, Comparer compare)
{
while (!st.empty() && compare(val, st.top()))
{
// do a bunch of stuff with st
}
}
}
How about something like this
class
{
public:
void func(const int val, const bool flag)
{
int sign = 1;
std::stack<int>* higher_or_lower = &higher;
if(flag)
{
higher_or_lower = &lower;
sign = -1;
}
while(!higher_or_lower->empty() && sign*val >= sign*higher_or_lower->top())
{
// do a bunch of stuff with higher_or_lower
}
}
private:
std::stack<int> lower;
std::stack<int> higher;
}
The higher_or_lower covers both stacks and the sign takes care of less than vs. greater than.
Or a bit more compact:
class C
{
public:
void func(const int val, const bool flag)
{
const std::stack<int>* st[] = {&lower, &higher};
bool (*compare[])(int, int) = { [](int a, int b) { return a <= b; } , [](int a, int b) { return a >= b; } };
while (!st[flag]->empty() && compare[flag](val, st[flag]->top()))
{
// do a bunch of stuff with *st
}
}
private:
std::stack<int> lower;
std::stack<int> higher;
};
I am creating a C++ class which takes certain parameters during initialization and has some functions based on its private variables, something like the compute function here:
class A {
public:
A(int x){
a = x;
}
int compute(int y){
if (a == 0){
return y*y;
}
else if (a == 1){
return 2*y;
}
else{
return y;
}
}
private:
int a;
};
// usage
A myA(1); // private variables set only once
myA.compute(10); // this will check value of a
myA.compute(1); // this will check value of a
Given that the private variables are set during initialization and will not be changed again, is there any efficient way to avoid the condition check related to the private variables during runtime?
Any and all assistance is appreciated. Thank you
You can template the function compute() on an int and use the template value as parameter. You can see the result at https://godbolt.org/z/14Mh4E
class A {
public:
A(int x) {
a = x;
}
template <int y>
constexpr int compute() const {
if (a == 0) {
return y * y;
}
else if (a == 1) {
return 2 * y;
}
else {
return y;
}
}
private:
int a;
};
// usage
A myA(1); // private variables set only once
myA.compute<10>(); // this will check value of a
myA.compute<1>(); // this will check value of a
You could avoid the condition check if you would use e.g. a function object as a member, and set this conditioned on the value of variable a.
Anyway, I don't think that the condition check will be big performance issue. But this will depend on your application of course.
#include <functional>
#include <iostream>
class A {
public:
A(int x)
: a { x }
{
if (a == 0){
compute = [](int y){ return y*y; };
}
else if (a == 1){
compute = [](int y){ return 2*y; };
}
else{
compute = [](int y){ return y; };
}
}
std::function<int(int)> compute;
private:
int a;
};
// usage
int main()
{
A myA(1); // private variables set only once
std::cout << myA.compute(10) << std::endl;
std::cout << myA.compute(1) << std::endl;
return 0;
}
You can guarantee the conditions are evaluated at compile time by using constexpr. Note that in this case you must use C++14 for constexpr compute(...), as multiple return statements are only suppoerted in constexpr functions after C++14.
#include <iostream>
class A {
public:
constexpr A(const int x): a(x) { }
constexpr int compute(const int y) const {
// Multiple return statements inside a constexpr function
// requires C++14 or above.
if (a == 0) {
return y*y;
}
else if (a == 1) {
return 2*y;
}
else {
return y;
}
}
private:
int a;
};
int main() {
constexpr A myA(1);
constexpr int num = myA.compute(123);
std::cout << num << std::endl;
return EXIT_SUCCESS;
}
This page contains a good explanation of constexpr, as well as examples.
If parameters are runtime value, I don't see an optimal way to avoid condition or jump.
You can trade your condition by virtual call:
struct A
{
virtual ~A() = default;
virtual int compute(int) = 0;
};
struct A0 { int compute(int y) override { return y * y; } };
struct A1 { int compute(int y) override { return 2 * y; } };
struct AN { int compute(int y) override { return y; } };
std::unique_ptr<A> makeA(int a)
{
switch (a) {
case 0: return std::make_unique<A0>();
case 0: return std::make_unique<A1>();
default: return std::make_unique<AN>();
}
}
(compiler might devirtualize the call if type is known at compile time)
or "equivalent":
struct A
{
int (*f)(int); // or even std::function<int(int)> f; if you need capture.
A(int a) : f(a == 0 ? +[](int y) { return y * y; }
: a == 1 ? +[](int y) { return 2 * y; }
: +[](int y) { return y; })
{}
int compute(int y) { return f(y); }
};
(erased-type is harder for compiler to devirtualize)
I post here to ask if there is a way to alternate different strategies of branching. Let me explain, I have an efficient branching strategy which we'll call the strategy A. The biggest problem is that the strategy A cannot be used that often. So when I cannot use the strategy A, I use another strategy, which I'll call the strategy B, which is less efficient.
The documentation says that:
Brancher order. Creating a brancher registers it with its home space. A space maintains a queue of its branchers in that the brancher that is registered first is also used first for
branching. The first brancher in the queue of branchers is referred to as the current brancher.
So, I supposed that if I post the brancher A then the brancher B, the brancher A will has priority and each time the status of A says there is no branching to do, the brancher B will be used. Seems like I was wrong because when the status of a brancher return false, it is never called again.
Here is a "minimal example":
#include <gecode/minimodel.hh>
#include <iostream>
using namespace Gecode;
using namespace std;
class MyChoice : public Choice {
public:
int pos; // Position of the variable
int val; // Value of to assign
MyChoice(const Brancher& b, int pos0, int val0)
: Choice(b,2), pos(pos0), val(val0) {}
// Report size occupied
virtual size_t size(void) const {
return sizeof(*this);
}
// Archive into e
virtual void archive(Archive& e) const {
Choice::archive(e);
e << pos << val;
}
};
class BranchA : public Brancher {
protected:
ViewArray<Int::IntView> x;
public:
BranchA(Home home, ViewArray<Int::IntView>& x0)
: Brancher(home), x(x0) {}
static void post(Home home, ViewArray<Int::IntView>& x) {
(void) new (home) BranchA(home,x);
}
virtual size_t dispose(Space& home) {
(void) Brancher::dispose(home);
return sizeof(*this);
}
BranchA(Space& home, bool share, BranchA& b)
: Brancher(home,share,b) {
x.update(home,share,b.x);
}
virtual Brancher* copy(Space& home, bool share) {
return new (home) BranchA(home,share,*this);
}
// status
virtual bool status(const Space& home) const {
for (int i=0; i<x.size(); i++)
if (!x[i].assigned())
return !i%2 && x[i].in(1);
return false;
}
// choice
virtual Choice* choice(Space& home) {
for (int i=0; true; i++)
if (!x[i].assigned())
return new MyChoice(*this,i,1);
GECODE_NEVER;
return NULL;
}
virtual Choice* choice(const Space&, Archive& e) {
int pos, val;
e >> pos >> val;
return new MyChoice(*this, pos, val);
}
// commit
virtual ExecStatus commit(Space& home,
const Choice& c,
unsigned int a) {
const MyChoice& pv = static_cast<const MyChoice&>(c);
int pos=pv.pos, val=pv.val;
if (a == 0)
return me_failed(x[pos].eq(home,val)) ? ES_FAILED : ES_OK;
else
return me_failed(x[pos].nq(home,val)) ? ES_FAILED : ES_OK;
}
};
void branchA(Home home, const IntVarArgs& x) {
if (home.failed()) return;
ViewArray<Int::IntView> y(home,x);
BranchA::post(home,y);
}
// BranchB //////////////////////////////////////////////////////
class BranchB : public Brancher {
protected:
ViewArray<Int::IntView> x;
public:
BranchB(Home home, ViewArray<Int::IntView>& x0)
: Brancher(home), x(x0) {}
static void post(Home home, ViewArray<Int::IntView>& x) {
(void) new (home) BranchB(home,x);
}
virtual size_t dispose(Space& home) {
(void) Brancher::dispose(home);
return sizeof(*this);
}
BranchB(Space& home, bool share, BranchB& b)
: Brancher(home,share,b) {
x.update(home,share,b.x);
}
virtual Brancher* copy(Space& home, bool share) {
return new (home) BranchB(home,share,*this);
}
// status
virtual bool status(const Space& home) const {
for (int i=0; i<x.size(); i++)
if (!x[i].assigned())
return i%2 && x[i].in(2);
return false;
}
// choice
virtual Choice* choice(Space& home) {
for (int i=0; true; i++)
if (!x[i].assigned())
return new MyChoice(*this,i,2);
GECODE_NEVER;
return NULL;
}
virtual Choice* choice(const Space&, Archive& e) {
int pos, val;
e >> pos >> val;
return new MyChoice(*this, pos, val);
}
// commit
virtual ExecStatus commit(Space& home,
const Choice& c,
unsigned int a) {
const MyChoice& pv = static_cast<const MyChoice&>(c);
int pos=pv.pos, val=pv.val;
if (a == 0)
return me_failed(x[pos].eq(home,val)) ? ES_FAILED : ES_OK;
else
return me_failed(x[pos].nq(home,val)) ? ES_FAILED : ES_OK;
}
};
void branchB(Home home, const IntVarArgs& x) {
if (home.failed()) return;
ViewArray<Int::IntView> y(home,x);
BranchB::post(home,y);
}
// Minimal Space ///////////////////////////////////////
class TestSpace : public Space {
protected:
IntVarArray x;
public:
TestSpace(int size)
: x(*this, size, 0, 10) {
branchA(*this, x);
branchB(*this, x);
}
TestSpace (bool share, TestSpace& s)
: Space(share, s) {
x.update(*this, share, s.x);
}
virtual Space* copy (bool share) {
return new TestSpace(share, *this);
}
void print(std::ostream& os) {
os << "x= " << x << endl;
}
};
// Minimal Main //////////////////////:
int main (int, char**) {
// create model and search engine
TestSpace* m = new TestSpace(10);
DFS<TestSpace> e(m);
delete m;
// search and print all solutions
while (TestSpace* s = e.next()) {
s->print(cout); delete s;
}
return 0;
}
In this example, the status of the brancher A return true if the next variable to assign is on an even index and if the variable can take the value of 1 (false else). And the brancher B status return true if the next variable to assign is on an odd index and if the variable can take the value of 2 (false else).
With that code I expected to get the solutions [1, 2, 1, 2, ...] and [!1, !2, !1, !2, ...] (and others combinations like [!1, 2, 1, !2, ...]) but since the branchers are disposed when their status return false, only the two first variables have been assigned.
Is there a good way to make the brancher not being disposed after its status return false (or to alternate two differents branching strategies) or should I merge the two branchers into one ?
If it may help someone, here is the solution I used.
As advised by Patrick Trentin, I unified the control by making a third brancher which is a vector of branchers. Here is the implementation I used:
The header branchAllInOne.h:
#include <gecode/minimodel.hh>
using namespace Gecode;
using namespace std;
class BranchAllInOne : public Brancher {
protected:
// Queue of brancher (may be better with ActorLink)
vector<Actor *> queue;
// Every brancher are in the brancher
BrancherGroup group;
mutable int toChoose;
class ChoiceAndID : public Choice {
public:
// Choice of the brancher used
Choice* c;
/// ID of brancher used
unsigned int id;
ChoiceAndID(const Brancher& b, Choice * c, unsigned int id);
virtual ~ChoiceAndID();
virtual size_t size(void) const ;
virtual void archive(Archive& e) const ;
};
public:
BranchAllInOne(Home home);
virtual size_t dispose(Space& home);
BranchAllInOne(Home home, bool share, BranchAllInOne& b);
virtual ~BranchAllInOne();
/**
* Check status of brancher, set toChoose value to the ID of the first
* brancher with alternative left
**/
virtual bool status(const Space&) const ;
/**
* Let the brancher of ID toChoose make the choice
*/
virtual Choice* choice(Space&);
virtual Choice* choice(const Space&, Archive& e);
/**
* Let the brancher of ID toChoose commit his choice
*/
virtual ExecStatus commit(Space& home, const Choice& _c, unsigned int a);
/// Copy brancher
virtual Actor* copy(Space& home, bool share);
/// Post brancher
static BranchAllInOne * post(Home home);
virtual void print(const Space& home,
const Choice& c,
unsigned int a,
ostream& o) const ;
void pushBrancher(Space& home, Brancher *b);
};
BranchAllInOne * branchAllInOne(Home home);
The implementation branchAllInOne.cpp:
#include "branchAllInOne.h"
static Brancher * ActorToBrancher(Actor *a);
// Choice implementation
BranchAllInOne::ChoiceAndID::ChoiceAndID(const Brancher& b, Choice * c0, unsigned int id0)
: Choice(b, c0->alternatives()),
c(c0),
id(id0){}
BranchAllInOne::ChoiceAndID::~ChoiceAndID() {
delete c;
}
size_t BranchAllInOne::ChoiceAndID::size(void) const {
return sizeof(*this) + c->size();
}
void BranchAllInOne::ChoiceAndID::archive(Archive& e) const {
Choice::archive(e);
c->archive(e);
}
BranchAllInOne::BranchAllInOne(Home home)
: Brancher(home),
toChoose(-1) {
home.notice(*this,AP_DISPOSE);
}
// brancher
BranchAllInOne * BranchAllInOne::post(Home home) {
return new (home) BranchAllInOne(home);
}
size_t BranchAllInOne::dispose(Space& home) {
home.ignore(*this, AP_DISPOSE);
size_t size = queue.size() * sizeof(Actor*);
for (unsigned int i = queue.size() ; i--;) {
size += ActorToBrancher(queue[i])->dispose(home);
}
queue.~vector();
// Making sure to kill each brancher inserted in the queue (may be useless)
group.kill(home);
(void) Brancher::dispose(home);
return sizeof(*this) + size;
}
BranchAllInOne::BranchAllInOne(Home home, bool share, BranchAllInOne& b)
: Brancher(home, share, b),
queue(b.queue.size()),
toChoose(b.toChoose){
for (unsigned int i = 0 ; i < queue.size() ; i++)
queue[i] = b.queue[i]->copy(home, share);
}
BranchAllInOne::~BranchAllInOne() {
for (unsigned int i = 0 ; i < queue.size() ; i++) {
delete queue[i];
}
queue.~vector();
}
Actor* BranchAllInOne::copy(Space& home, bool share){
return new (home) BranchAllInOne(home, share, *this);
}
// status
bool BranchAllInOne::status(const Space& s) const {
for (unsigned int i = 0 ; i < queue.size() ; i++) {
if (ActorToBrancher(queue[i])->status(s)) {
toChoose = i;
return true;
}
}
std::cout << std::endl;
return false;
}
// choice
Choice* BranchAllInOne::choice(Space& s) {
ChoiceAndID* res = new ChoiceAndID(*this,
const_cast<Choice *>(ActorToBrancher(queue[toChoose])->choice(s)),
toChoose);
toChoose = -1;
return res;
}
Choice* BranchAllInOne::choice(const Space& s, Archive& e) {
return new ChoiceAndID(*this,
const_cast<Choice *>(ActorToBrancher(queue[toChoose])->choice(s, e)),
toChoose);
}
// Perform commit for choice \a _c and alternative \a a
ExecStatus BranchAllInOne::commit(Space& home, const Choice& c, unsigned int a) {
const BranchAllInOne::ChoiceAndID& ch = static_cast<const BranchAllInOne::ChoiceAndID&>(c);
return ActorToBrancher(queue[ch.id])->commit(home, const_cast<Choice&>(*ch.c), a);
}
void BranchAllInOne::print(const Space& home,
const Choice& c,
unsigned int a,
ostream& o) const {
const BranchAllInOne::ChoiceAndID& ch = static_cast<const BranchAllInOne::ChoiceAndID&>(c);
o << ch.id << ": ";
ActorToBrancher(queue[ch.id])->print(home, *(ch.c), a, o);
}
void BranchAllInOne::pushBrancher(Space &home, Brancher *b) {
queue.push_back(b);
group.move(home, *b);
}
static Brancher * ActorToBrancher(Actor *a) {
return dynamic_cast<Brancher *>(a);
}
// end of BranchAllInOne implementation
BranchAllInOne* branchAllInOne(Home home) {
if (home.failed()) return NULL;
return BranchAllInOne::post(home);
}
I've made some modifications to get a pointer to branchers I want to put in the vector (that include the post function of each branchers):
brancherA example:
BranchA * BranchA::post(Home home, ViewArray<Int::IntView>& x) {
return new (home) BranchA(home,x);
}
BranchA * branchA(Home home, const IntVarArgs& x) {
if (home.failed()) return NULL;
ViewArray<Int::IntView> y(home,x);
return BranchA::post(home,y);
}
The space has also been modified:
TestSpace::TestSpace(int size)
: x(*this, size, 0, 10) {
BranchAllInOne * b = branchAllInOne(*this);
b->pushBrancher(*this, branchA(*this, x));
b->pushBrancher(*this, branchB(*this, x));
}
I tested it with and without Gist and only got a memory leak of a pointer for each brancher put in the vector (here only two). A small problem remain is that branchers put in the vector are also scheduled after the third brancher stoped (but their status return false).
Suppose I have a simple vector class where elements are accessed through a proxy class.
Vector class:
class vec {
public:
vec(int len) {
length = len;
data = new double [len];
}
proxy operator[](int i) {
if (i >= 0 && i < length) {
return proxy(i, data);
}
else {
std::cerr << "AHHHH!\n";
exit(1);
}
}
private:
int length;
double * data;
};
Proxy class:
class proxy {
public:
proxy(int i, double * d) {
index = i;
data = d;
}
void operator=(double rhs) {
data[index] = rhs;
}
private:
int index;
double * data;
};
How can I assign elements from the vector (or rather, from the proxy) to a variable of type double? In other words, how do I accomplish the following:
int main() {
vec a(2);
double x = 3.14;
a[0] = x; // Works!
x = a[0]; // How to make work?
return 0;
}
Unfortunately, I can't write something like:
friend double operator=(double & lhs, const proxy & p) { ... }
since operator= must be a member.
Add a conversion function to your proxy class:
class proxy
{
public:
operator double() const { return data[index]; }
// ...
};
I've been looking around and haven't come up with any tangible solutions. It sounds like it is looking for a default constructor instead of the one in place but I have one below. Moving it up as the first listed constructor didn't change the error messages so I'm wrong about that. Here's the full error message (using jGRASP):
In file included from intset.h:47:0,
from IntSet.cpp:1:
IntSet.cpp:12:11: error: expected unqualified-id before 'int'
IntSet(int a, int b, int c, int d, int e) {
^
IntSet.cpp:12:11: error: expected ')' before 'int'
Here's the IntSet.cpp code:
#include "intset.h"
//#include <algorithm>
//#include <iostream>
int size;
const int MAXSIZE = 25000;
bool set[MAXSIZE];
const int SENTINEL = -1;
//Constructors
IntSet(int a, int b, int c, int d, int e) {
size = a;
if(b > size) {
size = b;
}
if(c > size) {
size = c;
}
if(d > size) {
size = d;
}
if(e > size) {
size = e;
}
set = new bool[size];
for(int i = 0; i <= size; i++) {
if(i == a || i == b || i == c || i == d || i == e) {
insert(i);
} else {
remove(i);
}
}
}
IntSet(int a, int b, int c, int d) {
IntSet(a, b, c, d, -1);
}
IntSet(int a, int b, int c) {
IntSet(a, b, c, -1, -1);
}
IntSet(int a, int b) {
IntSet(a, b, -1, -1, -1);
}
IntSet(int a) {
IntSet(a, -1, -1, -1, -1);
}
//Copy constructor
IntSet(const IntSet& x) {
size = x.size;
for (int i = 0; i <= x.size; i++ ) {
set[i] = x.set[i];
}
}
//Destructor
~IntSet()
{
//for(int i = this.length(); i >= 0; i--) {
// this[i]
//}
}
////////////////////////
bool insert(int a) {
if(a <= size && a >= 0) {
set[a] = true;
return true;
}
else if(a >= 0) {
//removed "new" from line below
IntSet temp = IntSet(a);
&this += temp;
set[a] = true;
return true;
}
return false;
}
bool remove (int a) {
if (isInSet(a)) {
set[a] = false;
return true;
}
return false;
}
bool isEmpty() {
bool retVal = true;
for (int i = 0; i <= size; i++) {
if (set[i] == true) {
retVal = false;
}
}
return retVal;
}
bool isInSet (int a) {
if (set[a]){
return true;
}
return false;
}
/////////////////////////////////////////////
IntSet operator + (IntSet a) {
IntSet c = IntSet(max(size, a.size));
for (int i = 0; i <= c.size; i++) {
if (set[i] || a.set[i]){
c.set[i] = true;
}
else {
c.set[i] = false;
}
}
return c;
}
IntSet operator * (IntSet a) {
IntSet c = IntSet(max(size, a.size));
for (int i = 0; i <= c.size; i++) {
if (set[i] && a.set[i]) {
c.set[i] = true;
}
else {
c.set[i] = false;
}
}
return c;
}
IntSet operator - (IntSet a) {
IntSet c = IntSet();
c.size = 0;
for (int i = 0; i <= size; i++) {
if (set[i] && !a.set[i]) {
c.set[i] = true;
}
else {
c.set[i] = false;
}
c.size++;
}
return c;
}
IntSet operator = (const IntSet a) {
return IntSet(a);
}
IntSet operator += (IntSet a) {
return IntSet(operator+(a));
}
IntSet operator *= (IntSet a) {
return IntSet(operator * (a));
}
IntSet operator -= (IntSet a) {
return IntSet(operator - (a));
}
IntSet operator == (const IntSet a) const{
for(int i = 0; i < size; i++) {
if(set[i] != a.set[i]) {
return false;
}
}
return true;
}
IntSet operator != (IntSet a) {
for(int i = 0; i < size; i++) {
if(set[i] != a.set[i]) {
return true;
}
}
return false;
}
IntSet operator << (IntSet a) {
cout << "{";
for(int i = 0; i < size; i++) {
if(set[i]) {
cout << " " << i;
}
}
cout << "}";
}
IntSet operator >> (IntSet a) {
int index;
while(cin >> index && index != SENTINEL) {
insert(index);
}
}
Here is the attached intset.h code:
#ifndef INTSET_H
#define INTSET_H
#include <iostream>
#include <algorithm>
using namespace std;
class IntSet {
public:
//Constructors
IntSet();
IntSet(int);
IntSet(int, int);
IntSet(int, int, int);
IntSet(int, int, int, int);
IntSet(int, int, int, int, int);
IntSet(const IntSet&); // M: Added the &; must be a pointer or reference
~IntSet();
//Overloaded Operators M: Added 'IntSet' in front of the word 'operator.'
// It was required syntax.
IntSet operator+(IntSet);
IntSet operator*(IntSet);
IntSet operator-(IntSet);
IntSet operator=(IntSet);
IntSet operator+=(IntSet);
IntSet operator*=(IntSet);
IntSet operator-=(IntSet);
IntSet operator==(IntSet);
IntSet operator!=(IntSet);
IntSet operator<<(IntSet);
IntSet operator>>(IntSet);
//Functions
bool insert(int);
bool remove(int);
bool isEmpty();
bool isInSet(int);
private:
const int MAXSIZE;
int size;
bool set[];
const int SENTINEL;
};
#include "IntSet.cpp"
#endif
I haven't had much experience with header files so it wouldn't surprise me if I formatted something incorrectly but I'm looking at plenty of other samples provided by the professor and there isn't anything unusual about mine. I thought maybe it had something to do with the order listed in the .h file and that I wasn't following the same exact order in the .cpp but nothing changed when I had everything listed in the same order.
There is a lot that is wrong with your code. We are going to have to jump around a bit between the header and the implementation. Ready?
In your header you do this:
class IntSet {
/* stuff */
private:
bool set[];
};
First of all, the name set is a bad choice: it is the name of a class in namespace stdw which you are importing by having using namespace std in your header file. This can be confusing at best.
More importantly, the syntax bool set[] isn't correct in this context. Even if your compiler allows it, it's an extension. Who knows what it does and how it will behave on other compilers? Avoid it.
If you want to declare an array, declare an array. If you want to declare a pointer, declare a pointer. Just remember: an array isn't a pointer.
Unfortunately you don't, becase later on in your code you do this:
set = new bool[size];
What is this supposed to do? set isn't a pointer, it's some kind of array, and you cannot assign a pointer to an array.
Now, we get to the second problem: you declare some member variables for your class, in your header file:
class IntSet {
/* some stuff here */
private:
const int MAXSIZE;
int size;
bool set[];
const int SENTINEL;
};
Then in your implementation you have the following code floating up at the top:
int size;
const int MAXSIZE = 25000;
bool set[MAXSIZE];
const int SENTINEL = -1;
I don't think that this does what you think it does. It seems that your intention is to initialize those variables, but that's not what happens. Remember, those variables only exist as members variables that belong to a particular instance of a class, and they are not "free-standing". So what's happening here?
Well, this declares all these variables again, so you have variables called MAXSIZE, size, set and SENTINEL that are valid anywhere in that translation unit (i.e. the .cpp file), independent of the member variables in the class.
This, of course, means that the member variables with those names aren't initialized (well, except set which you assign a pointer to, which we already know is wrong). This will cause your code to exhibit undefined behavior. After all, the value of an uninitialized variable can be anything at all.
If your intention had been to initialize the class members, then you should remove that code and initialize those variables in your constructor(s):
IntSet::IntSet(int a, int b, int c, int d, int e)
: size(a), MAXSIZE(25000), SENTINEL(-1)
{
/* whatever*/
}
Notice, by the way, how I used IntSet:: in front of the constructor name? This is called the scope resolution operator. Remember, there is no constructor called IntSet. The constructor belongs to a class, which is called IntSet, and outside of that class, it's proper name is IntSet::IntSet. A small example may help:
class Test
{
int Length;
public:
/* notice how inside the class, you only need Test
* when providing a body for the constructor. This
* makes sense. You know which class you inside of.
*/
Test()
: Length(0)
{
}
Test(int len);
};
/* Now we are outside the class. We need to help
* the compiler out and tell it what class the
* function belongs to.
*/
Test::Test(int len)
: Length(len)
{
}
A tangential point as to do with the names that you are using. What's a? Why do you use a to initialize something called size? You should choose meaningful variables names that help document the code so that when you have to read it back your head doesn't explode.
Another tangential point is that if variables like MAXSIZE and SENTINEL are going to be shared between all instances of the class, then, for future reference, you should probably consider making them static class members.
Lastly, you have this bit of code in your header file
#include "IntSet.cpp"
This is, almost certainly, not correct. You should never do this (there may be some who think that there are exceptions, but don't learn bad habits at this point. When you know enough to stumble on this legitimately, then you will know enough to determine whether it's the right thing to do or not).
What makes it worse is that your implementation file contains:
#include "IntSet.h"
Think about what you are doing here: when the compiler is processing the file IntSet.h you are telling to also process the file IntSet.cpp. The file IntSet.cpp tells the compiler to process the file IntSet.h. Which tells the compiler to process the file IntSet.cpp. And so on and so forth.
Generally speaking, implementation files (.cpp) will include header files. Header files will only include other header files.
There are a few other issues, but you should probably fix all these things, and then, if you are still having issues, post a new question and we can go from there.
Good luck!
You need to put the name of the class and :: before defining a member function.
IntSet::IntSet(int a, int b, int c, int d, int e) {
//^^^^^^^^
//here
Do the same with the other constructors, the operators and methods.