It is said that the arrow operator is applied recursively. But when I try to execute the following code, it prints gibberish when it is supposed to print 4.
class dummy
{
public:
int *p;
int operator->()
{
return 4;
}
};
class screen
{
public:
dummy *p;
screen(dummy *pp): p(pp){}
dummy* operator->()
{
return p;
}
};
int main()
{
dummy *d = new dummy;
screen s(d);
cout<<s->p;
delete d;
}
What Stanley meant by “recursive” is just that the operator is applied to every returned object until the returned type is a pointer.
Which happens here on the first try: screen::operator -> returns a pointer. Thus this is the last call to an operator -> that the compiler attempts. It then resolves the right-hand sice of the operator (p) by looking up a member in the returned pointee type (dummy) with that name.
Essentially, whenever the compiler finds the syntax aᵢ->b in code, it essentially applies the following algorithm:
Is aᵢ of pointer type? If so, resolve member b of *aᵢ and call (*aᵢ).b.
Else, try to resolve aᵢ::operator ->
On success, set aᵢ₊₁ = aᵢ::operator ->(). Goto 1.
On failure, emit a compile error.
I’m hard-pressed to come up with a short, meaningful example where a chain of operator -> invocations even makes sense. Probably the only real use is when you write a smart pointer class.
However, the following toy example at least compiles and yields a number. But I wouldn’t advise actually writing such code. It breaks encapsulation and makes kittens cry.
#include <iostream>
struct size {
int width;
int height;
size() : width(640), height(480) { }
};
struct metrics {
size s;
size const* operator ->() const {
return &s;
}
};
struct screen {
metrics m;
metrics operator ->() const {
return m;
}
};
int main() {
screen s;
std::cout << s->width << "\n";
}
C++ Primer (5th edition) formulates it as follows on page 570:
The arrow operator never loses its fundamental meaning of member access. When we overload arrow, we change the object from which arrow fetches the specified member. We cannot change the fact that arrow fetches a member.
The deal is once screen::operator->() returns a pointer (dummy*) the recursion stops because built-in (default) -> in used on that pointer. If you want recursion you should return dummy or dummy& from screen::operator->()
Related
I have the following code snippet:
QList<const GLMeshPtr*> meshList;
m_itemsList->getSelectedMeshes(meshList);
for (auto m : meshList)
{
if (GLBodyPtr c = (*m)->getStandard()) {
c->executeMeshFix();
}
}
GLMeshPtr is set as in this typedef:
typedef std::shared_ptr<GLMesh> GLMeshPtr;
The definition of m_itemsList->getSelectedMeshes is:
void QMeshList::getSelectedMeshes(QList<const GLMeshPtr*>& list)
{
for (auto& m : m_meshList) {
if (m->isSelected()) {
list.push_back(m->getGLMesh());
}
}
}
Definition for getGLMesh is:
const GLMeshPtr* getGLMesh() const { return &m_glmesh; } // where m_glmesh is a GLMeshPtr.
My question is very simple, yet, I couldn't find any reference to it. Does the * keyword create a copy of the value in the stack, necessarily, or does it use the value "in place"?
I talk more specifically about this line:
if (GLBodyPtr c = (*m)->getStandard()) {
Am I creating unnecessary copies of GLBodyPtr? I don't want anyone here to share the pointer.
No, the * operator returns a reference, i.e. GLBodyPtr const&. It is const because the pointer is declared to point to a const object. See definition of std::shared_ptr::operator* on cppreference.com.
References are basically like pointers that can't be changed once they're initialized, so it's not a copy of the shared_ptr object.
I overloaded array subscript ( [] ) operator. I have made it return an integer as I wont be using it for any assignment purposes. However, I am unable to use the comparison operator now!
Here is the code
class Set
{
public:
virtual int operator[](int i) = 0;
virtual int size() = 0;
void union_operation(Set* second);
void interesction_operation(Set* second);
};
void Set::union_operation(Set* second)
{
int second_size = second->size();
for(int i=0;i<second_size;i++)
{
for(int j=0;j<this->size();j++)
{
//The line below doesnt work!
if(this[j]==second[i])
{
break;
}
}
}
}
The implementation of operator overloading is carried out in a derived class.
Since the overloaded operator will return an integer, hence the comparison is between two integers, which is perfectly valid. Why does this line still give an error?
In C++, this is a pointer that requires dereferencing before you can use it. Unless you're passing it to a function of course.
So, in order for your comparison to work, it should look like the following:
if((*this)[j] == (*second)[i])
{
break;
}
EDIT: second is also a Set pointer so you must dereference it to use it too.
I'm sorry if this has been asked already, but I'm still learning C++ and struggling with a bit of syntax.
I'm supposed to overload the type conversion operator, so that it will accept an object and return an int value, based on a protected int inside that object.
Header file:
definitions.h
class Baseballs
{
protected:
int currentValue;
public:
Baseballs(int);
int operator= (Baseballs&); // ??????
}
Methods:
methods.cpp
#include "definitions.h"
Baseballs::Baseballs(int value)
{
currentValue = value;
}
int Baseballs::operator=(Baseballs &obj) // ??????
{
int temp = obj.currentValue;
return temp;
}
So in main.cpp, if I create an object:
Baseballs order(500);
Then 500 is assigned to currentValue. I need to be able to assign that to an int variable, and ultimately print it for verification, such as:
int n = order;
cout << n;
What I'm having trouble with is the syntax for overloading =. Can someone tell me what the proper syntax for the definition and method should be?
The overloaded = is really to assign to objects of the same type. Ex:
order = another_order;
What you are looking for is an overloaded conversion operator.
operator int() { return currentvalue; }
However this is generally not regarded as good practice, due to unknown conversions. An explicit overload is much safer:
explicit operator int() {...}
However you would need to do:
int n = static_cast<int>(order);
When I define a function of a class, I call another function of the same class within it. But when I do not type the class name it gives segmentation fault. Check below.
Header file
class DomainSolver
{
int fnc1 (UserDefinedType & var);
int fnc2 (UserDefinedType & var);
};
C file
int DomainSolver::fnc2 (UserDefinedType & var)
{
return 0;
}
int DomainSolver::fnc1 (UserDefinedType & var)
{
// fnc2 (var); // does not work
DomainSolver::fnc2(var); // works
return 0;
}
Wild guess… since the code you presented does not have any issues…
The function being called is declared virtual in a base class, so even if the virtual keyword is not present in the declaration here it is virtual.
The function being called does not access any member of the object.
You are calling the function on an invalid pointer or reference (for example through a null pointer or on an object that has already been deleted.
If all those guesses are right, the use of the qualification inhibits the dynamic dispatch mechanism, avoiding the dereference of an invalid pointer to the vtable. The code is still wrong (due to the third point above), but it seems to work.
The solution is not to call a member function through an invalid pointer or reference.
Although as pointed out by Zac's reply, the functions as you present them are not properly formed, there shouldn't be a difference between calling the scoped version; if you are getting a segfault in one case and not the other it's possibly because of code elsewhere.
Here is an example that works just fine:
dsolver.hh
#ifndef DSOLVER_HH
#define DSOLVER_HH
class DomainSolver
{
public:
int fnc1 (int& var);
int fnc2 (int& var);
};
#endif
dsolver.cc
#include <iostream>
#include "dsolver.hh"
int DomainSolver::fnc1 (int& var)
{
std::cout << "fnc1\n";
fnc2( var );
var = -1;
return var;
}
int DomainSolver::fnc2 (int& var)
{
std::cout << "fnc2\n";
var = 100;
return var;
}
main.cc
#include <iostream>
#include "dsolver.hh"
int main()
{
DomainSolver my_dsolver;
int my_int = 5;
my_dsolver.fnc1(my_int);
return 0;
}
Assuming this is close to your actual code, you have undefined behavior in fnc1:
int DomainSolver::fnc1 (UserDefinedType & var)
{
// fnc2 (var); // does not work
DomainSolver::fnc2(var); // works
// missing return!!!
}
You declare it to return an int, but then never return anything (in either case). Both cases are UB, so anything they do is technically "valid", since your code is not.
This code should be:
int DomainSolver::fnc1 (UserDefinedType & var)
{
return fnc2 (var);
}
As a side note: This is a good example of why you should never ignore the warnings given by the compiler (as you should have received a warning with both versions).
EDIT
With your latest edit adding a return value to fnc1, you'll need to show more of your actual code as there is not enough there to properly diagnose the problem (with the return being there, there is nothing wrong with your shown code).
I have the following structure:
struct CountCarrier
{
int *CurrCount;
};
And this is what I want to do:
int main()
{
CountCarrier carrier = CountCarrier();
*(carrier.CurrCount) = 2; // initialize the *(carrier.CurrCount) to 2
IncreaseCount(&carrier); // should increase the *(carrier.CurrCount) to 3
}
void IncreaseCount(CountCarrier *countCarrier)
{
int *currCounts = countCarrier->CurrCount;
(*currCounts)++;
}
So, my intention is specified in the comments.
However, I couldn't get this to work. For starters, the program throws an exception at this line:
*(carrier.CurrCount) = 2;
And I suspect the following line won't work as well. Anything I did wrong?
struct CountCarrier
{
int *CurrCount; //No memory assigned
};
You need to allocate some valid memory to the pointer inside the structure to be able to put data in this.
Unless you do so, What you ar trying to do is attempting to write at some invalid address, which results in an Undefined Behavior, which luckiy in this case shows up as an exception.
Resolution:
struct CountCarrier
{
int *CurrCount; //No memory assigned
CountCarrier():CurrCount(new(int))
{
}
};
Suggestion:
Stay away from dynamic allocations as long as you can.
When you think of using pointers always think whether you really need one. In this case it doesn't really seem that you need one, A simple int member would be just fine.
You need to create the pointer. ie. carrier->CurrCount = new int;
*(carrier.CurrCount)
This is dereferencing the pointer carrier.CurrCount, but you never initialized it. I suspect this is what you want:
carrier.CurrCount = new int(2);
I seriously doubt that your program throws an exception at the line:
*(carrier.CurrCount) = 2;
While throwing an exception is certainly allowed behaviour, it seems much more likely that you encountered an access violation that caused the process to be killed by the operating system.
The problem is that you are using a pointer, but your pointer is not initialised to point at anything. This means that the result of the pointer dereference is undefined.
In this situation there does not seem to be any advantage to using a pointer at all. Your CurrCount member would work just as well if it was just a plain int.
If you are using C++, then you should encash its facilities. Instead of correcting your code, I am showing here that how the code should look like:
struct CountCarrier
{
int CurrCount; // simple data member
CountCarrier(int count) : CurrCount(count) {} // constructor
CountCarrier& operator ++ () // overloaded operator
{
++ CurrCount;
return *this;
}
};
We are overloading operator ++, because you have only one data member. You can replace with some named method also, like void IncrementCount().
CountCarrier carrier(2);
++ carrier;
As Als said, you need to provide some memory for the code to work.
But why make it so complicated? You don't need any pointers for the code you have to work. The "modern C++" way looks more like this:
struct CountCarrier
{
public:
CountCarrier(int currCount) : currCount(currCount) {}
void IncreaseCount() { ++currCount; }
int GetCount() const { return currCount; }
private:
int currCount;
};
int main()
{
CountCarrier carrier(2); // Initialize carrier.currCount to 2
carrier.IncreaseCount(); // Increment carrier.currCount to 3
}
Note how much cleaner and less error prone that is. Like I said, pick up a good introductory C++ book and read through it.