class A
{
public:
A* operator->() const
{
}
void Test() {}
};
then call it like this.
A* a = new A;
a->Test();
The code builds and runs successfully in VC2010. It seems very strange. I am wondering it is by design or it is a bug of VC2010?
Thanks
You aren't calling your operator-> in your example, you are calling Test directly from an A*. You want:
(*a)->Test();
Or...
A a;
a->Test();
There's nothing wrong with VS2010 concerning operator-> (that I know of).
Using -> on a pointer named a effectively executes: (*a).. Using -> on a variable by value will invoke your operator-> if present, or be a syntax error if there is no operator->.
Your code is:
A* a = new A;
a->Test();
The "a" is a pointer to an A. It is NOT an A object itself, it is a memory address of the A object on the heap.
When you call a->Test() you invoke the pointer's -> operator (built in for all pointer types in C++). You would have to do this to invoke your operator:
//Create an A (NOT a pointer).
A a;
//Invoke the A's operator ->
a->Test();
Which is how STL iterators work - they're class types, not pointers to class types. Note that the return type of operator -> must make sense for the operation/member you're trying to invoke.
So, here's an example that would call test via the ->:
#include <iostream>
class A
{
public:
A* operator->()
{
return this;
}
void Test() { std::cout << "Hello World!"; }
};
int main()
{
A a;
a->Test();
}
It's strange, but it works because a->Test(); returns the current object, which Test() is then called on (see the return this; line).
See STL iterators for useful examples of why you'd actually want to do this.
Related
I was looking at overloading the -> operator. I came up with the following simple example:
struct foo
{
int p = 12;
};
struct A
{
foo* f;
A()
{
this->f = new foo();
}
foo* operator-> ()
{
return f;
}
};
int main()
{
A a;
std::cout << a->p; //output 12
}
Although this example works, yet I am hoping if someone could tell me why it works.
I was thinking that it should work like this
std::cout << a->->p; //The first arrow returns the pointer
//the second arrow dereferences it
but here it seems like a single arrow not only returns the pointer but it also dereferences it. Is this a special case in C++?
Yes, this is by-design, operator-> will be called recursively on the return value; then we can use such class (so-called smart pointers) in the same way as raw pointers.
If a user-defined operator-> is provided, the operator-> is called again on the value that it returns, recursively, until an operator-> is reached that returns a plain pointer. After that, built-in semantics are applied to that pointer.
I have recently seen a question on stack overflow that how to get derived object from function,
some suggested that create local object and return copy from function. how about returning this from function?
I just wanna know, is that good coding practice?
Thank you for your assistance and time.
below is my example code.
class Base {
public:
virtual ~Base() {}
};
class Derived: public Base
{
private:
int i;
public:
Derived* func(int e) {
i = e;
return this;
}
int getI() { return i; }
};
You could use your code like this
Derived d;
Derived* d_ptr = d.func(123);
Seems easier to just write this (assuming a suitable constructor)
Derived x(123);
Derived* x_ptr = &x;
But maybe you were thinking of something else.
Derived d;
Derived* d_ptr = d.func(123);
why not d_ptr = &d;
whey d.func returns this? when use d.func, user have the pointer of d. no need to return again. may be want to use in chain like d.func(1)->func(2)->func(3)
Several operators used to return self reference (but not pointer):
copy/move assignment
pre increment/decrement
operator +=, operator -=, ...
mainly to mimic built-in behavior.
That allows to chain operation: a = b = c = 42; (instead of c = 42; b = c; a = b;).
(Abusing of) chaining is not necessary more readable, and split in several statements may be clearer.
The pro-chaining also apply it to setter:
rectangle.set_height(42).set_width(21).set_position(x, y);
About pointer versus reference, returning pointer implies generally that nullptr is possible value (else reference is better).
operators which might return pointer are (address of) operator& and arrow operator->.
Behavior of default & already returns this, so no need to overload it to return this.
operator-> should, at the end, returns a pointer; this might be a valid choice depending of the wrapper class.
I tried looking for an answer, but couldn't find anything helpful to understand what happens behind the scenes.
Assuming that we have the following:
class Base
{
public:
virtual int request() = 0;
void* operator new(long unsigned int size) = delete;
};
class Derived final : public Base
{
public:
int request() override;
};
int Derived::request()
{
return 2;
}
On the base class I have deleted the new operator because I don't want to be able to create pointers.
I know it might not make any sense, it is just an experiment.
When I call g++ gives me a compilation error to tell me that the new operator is deleted. Exactly what I expected.
int main()
{
auto d = std::make_unique<Derived>();
int value = d->request();
std::cout << "Request value is " << value << "." << std::endl;
return 0;
}
But if I create a shared_ptr the code compiles and runs fine.
auto d = std::make_shared<Derived>();
Why is this code valid when creating shared pointers. The operator is not being used in this case?
std::make_shared use global placement new version of the operator new, by std::allocator::construct
It is necessary to store object and atomic reference counter in the same block of memory, so that shared_ptr template works almost equal to intrusive smart pointer.
You can not prevent std::make_shared to construct a smart pointer.
So I think I might be overthinking this but I wanted to know if someone could clarify why the following statement works in the given code
f->hello();
This is the code
struct bar
{
void hello()
{
std::cout << "Hello World";
}
};
struct foo
{
bar* f;
foo() {
f = new bar();
}
~foo() {
delete f;
}
bar* operator->() {
return f;
}
};
int main()
{
foo f;
f->hello(); //Works
}
Since the following code from above returns a pointer
bar* operator->() {
return f;
}
should'nt
f->hello(); actually be f->->hello();
why does f->hello() work and f->->hello() fails ?
The reason i am thinking that f->->hello() should work is because
f->ptrReturned->hello();
If we overload the -> operator are we required to return a ptr type ? Form what I have tried it seems returning an object type is not allowed
Your understanding is correct basically, but ->-> is not valid syntax. You can use the overloaded operator-> like
f.operator->()->hello();
//^^^^^^^^^^^^ return the pointer
// ^^^^^^^^^ call hello() on the returned pointer
f->hello() is treated as the same of f.operator->()->hello(); that makes the usage of class with overloaded operator-> (e.g. smart pointers) consistent with built-in pointers. The usage would be more natural, and could be used with raw pointers in more general context like templates.
And,
If we overload the -> operator are we required to return a ptr type ?
There're some restrictions:
The overload of operator -> must either return a raw pointer, or return an object (by reference or by value) for which operator -> is in turn overloaded.
Apparently, that's just how an overloaded operator-> works. The part before the -> (not including the operator itself) gets replaced with the return value of the overloaded operator. For example, std::unique_ptr::operator-> returns a pointer, which then gets dereferenced by the ->.
I have't coded in c++ for some time and I got stuck when I tried to compile this simple snippet:
class A
{
public:
void f() {}
};
int main()
{
{
A a;
a.f(); // works fine
}
{
A *a = new A();
a.f(); // this doesn't
}
}
It's a pointer, so instead try:
a->f();
Basically the operator . (used to access an object's fields and methods) is used on objects and references, so:
A a;
a.f();
A& ref = a;
ref.f();
If you have a pointer type, you have to dereference it first to obtain a reference:
A* ptr = new A();
(*ptr).f();
ptr->f();
The a->b notation is usually just a shorthand for (*a).b.
A note on smart pointers
The operator-> can be overloaded, which is notably used by smart pointers. When you're using smart pointers, then you also use -> to refer to the pointed object:
auto ptr = make_unique<A>();
ptr->f();
Allow an analysis.
#include <iostream> // not #include "iostream"
using namespace std; // in this case okay, but never do that in header files
class A
{
public:
void f() { cout<<"f()\n"; }
};
int main()
{
/*
// A a; //this works
A *a = new A(); //this doesn't
a.f(); // "f has not been declared"
*/ // below
// system("pause"); <-- Don't do this. It is non-portable code. I guess your
// teacher told you this?
// Better: In your IDE there is prolly an option somewhere
// to not close the terminal/console-window.
// If you compile on a CLI, it is not needed at all.
}
As a general advice:
0) Prefer automatic variables
int a;
MyClass myInstance;
std::vector<int> myIntVector;
1) If you need data sharing on big objects down
the call hierarchy, prefer references:
void foo (std::vector<int> const &input) {...}
void bar () {
std::vector<int> something;
...
foo (something);
}
2) If you need data sharing up the call hierarchy, prefer smart-pointers
that automatically manage deletion and reference counting.
3) If you need an array, use std::vector<> instead in most cases.
std::vector<> is ought to be the one default container.
4) I've yet to find a good reason for blank pointers.
-> Hard to get right exception safe
class Foo {
Foo () : a(new int[512]), b(new int[512]) {}
~Foo() {
delete [] b;
delete [] a;
}
};
-> if the second new[] fails, Foo leaks memory, because the
destructor is never called. Avoid this easily by using
one of the standard containers, like std::vector, or
smart-pointers.
As a rule of thumb: If you need to manage memory on your own, there is generally a superiour manager or alternative available already, one that follows the RAII principle.
Summary: Instead of a.f(); it should be a->f();
In main you have defined a as a pointer to object of A, so you can access functions using the -> operator.
An alternate, but less readable way is (*a).f()
a.f() could have been used to access f(), if a was declared as:
A a;
a is a pointer. You need to use->, not .