Pointers have little to no use

Pages: 12345
closed account (N36fSL3A)
Sometimes you don't want that overhead when you're writing performance critical applications.
I don't consider optimization. I'm in the process of writing the article on that subject, so just wait a bit.
Then what are you saying?
Well, you replied with "I was careful when I titled the article" to "Your argument is that pointers have little use", so I guess I'm saying that you said that pointers have little use, which makes this entire exchange pointless, in my view. Honestly, I don't even know why your brought it up.

In C, compare the frequency of use cases for pointers that I ignore to the frequency of use cases for pointers that I don't ignore. (I'm also very biased against C)
What does this even mean? And who's talking about C? I'm talking about interfacing with C.

OK, I'll let you explain your mysterious byte-level manipulations with comments instead.
Well, if I'm twiddling bits, comments -- and not extra overhead -- is probably where explanations should come from.

Then why did you even ask?
I wanted to know if eliminating pointers was a mean or an end to you. Since you seem to think that elimination (almost) invariably makes the code better, there isn't much we can discuss. I'm not so confident that pointers are optional.
I guess I should clarify: I don't consider wrapper implementations either. Interfacing with C falls in the wrapper implementation category.

I already stated my stance on optimization and will do so more thoroughly in an article.

Pointers are not optional for implementing wrappers - there's no argument there.
LB wrote:
Disch, you're asking me what advantage references offer over pointers? Haven't you personally explained this to people on these forums many times?


I wasn't asking rhetorically. I'm genuinely curious.

And I'm not aware of any 'advantage' a reference has over a pointer -- they're merely syntactic differences. I've explained the difference to people on these forums, and may have recommended usage of references over pointers for a particular task... but not either of the tasks I mentioned.

I consider nicer syntax to be an advantage, though I guess that's an opinion. More importantly, though, the advantages are more abstract and have to do with the connotations of pointers vs the connotations of references. Just because you can use some language feature a certain way doesn't mean you should, and this applies to more than you would think.
closed account (D80DSL3A)
One remark on an important difference I see between references and pointers. Pointers are re assignable and references are not. Try doing a linked list with references.
An object with a reference as a data member must have that member assigned on creation:
A::A(& rB): myBref(rB)// initialization list must be used here
Whereas the assignment (object association) may be postponed when a pointer is involved.
A::A(B* pB = nullptr): myBpointer(pB)// assign later as needed.
Example: Player fires a homing shot targeting targetB:
shot.pTarget = &targetB;

Just wanted to point out that difference.
Last edited on
closed account (D80DSL3A)
OK. I see that removes the restriction. Interesting hybrid behavior and syntax. Thanks.

One question: From the example we have:
1
2
3
4
5
6
// reference_wrapper assignment:
  a=10; b=20;
  std::reference_wrapper<int> wrap1 (a);
  std::reference_wrapper<int> wrap2 (b);

  wrap1 = wrap2;// changing which int is referenced by wrap1. Similar to pointer assignment. 

How would I copy values from b to a (make a=20) using wrap1 and wrap2 ?
eg. using pointers we have:
pA = pB;// change which object is pointed to.
vs.
*pA = *pB;// just copy the value. How with std::reference_wrapper ?

EDIT: NVMD. I see the get() which returns a & to the value.
So that would be: wrap1.get() = wrap2.get();

EDIT2: Furthermore I see you can have an array of std::reference_wrapper, overcoming yet another restriction on regular references vs. pointers.

Is there a way to test if 2 ref-wrappers refer to the same object like pA == pB with pointers ? I don't see an operator== overload.
Last edited on
fun2code wrote:
Is there a way to test if 2 ref-wrappers refer to the same object like pA == pB with pointers ? I don't see an operator== overload.
&wrap1() == &wrap2()? I'm not sure why you'd want such a test but it's possible.
closed account (D80DSL3A)
OK. Thanks.
Tests for self assignment crop up. eg.
1
2
3
4
5
operator=(const A& rA)
{
    if( this == &rA ) return *this;// no copy needed. *this and rA are the same instance of A
    //....
}
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <functional>
#include <type_traits>
#include <memory>
#include <iostream>

template < typename T, typename U > 
void do_compare_( const std::reference_wrapper<T>& a, const std::reference_wrapper<U>& b, const char* a_tag, const char* b_tag )
{
    if( std::is_same< typename std::remove_cv<T>::type, typename std::remove_cv<U>::type >::value )
    {
        if( std::addressof(a) == static_cast<const volatile void*>( std::addressof(b) ) ) 
            std::cout << a_tag << " and " << b_tag << " are the same object\n" ;
            
        else if( std::addressof( a.get() ) == std::addressof( b.get() ) ) 
            std::cout << a_tag << " and " << b_tag << " are different objects, but they wrap references to the same object \n" ;
            
        else if( a == b )
            std::cout << a_tag << " and " << b_tag << " wrap reference to two different objects of the same unqualified type"
                                                      " which compare aqual\n" ;
    }
}

#define compare(a,b) do_compare_( a, b, #a, #b ) 

int main()
{
    int i = 7 ;
    int j = 7 ;
    
    auto a = std::ref(i) ; 
    const auto& b = a ;
    auto c = std::ref(i) ;
    auto d = std::ref(j) ;
    
    const auto e = std::cref(i) ;
    auto f = std::cref(j) ;
    
    compare(a,b) ; // a and b are the same object
    compare(a,c) ; // a and c are different objects, but they wrap references to the same object
    compare(a,d) ; // a and d wrap reference to two different objects of the same unqualified type which compare aqual
    compare(a,e) ; // a and e are different objects, but they wrap references to the same object 
    compare(a,f) ; // a and f wrap reference to two different objects of the same unqualified type which compare aqual
}

http://coliru.stacked-crooked.com/a/d13c07f0dce5806b


This entire discussion is very appropriate for the lounge.

Pointers Oil rigs and refineries have little to no use (because we can use vectors and reference wrappers go to the petrol station and buy petrol).
Last edited on
You should look at std::reference_wrapper ;)
http://www.cplusplus.com/reference/functional/reference_wrapper/operator=/

The problem I see with your stance is that you may be assuming that others are or should have an equal knowledge of C++ features as you do. Most people don't know what reference wrapper is and will be confused by a code snippet that uses it. That's probably ok because they will look it up and figure it out. But the fact is that things like that require reading documentation to understand. Even people who know what it is will need to check the documentation now and then to be sure exactly what it does and how it's used. When very little is actually achieved by replacing a pointer with something like that, what you are actually doing is making the code harder to read/understand, and take longer to write.

I think that a lot of people overuse high level C++ features and the result is overly complicated code. This is why I would discourage these types of ideologies.

Last edited on
I kind of agree with LB. But this is nothing suprising, really. C++ is trying very hard to duplicate every possible feature of C with a "better" version, and pointers are no exception.
C++ is trying very hard to duplicate every possible feature of C with a "better" version, and pointers are no exception.


I lolled.
closed account (EwCjE3v7)
What about iterators, they are very handy and are like pointers and pointers to elements in an array
I lolled.
I'm glad I was able to entertain you.

EDIT: Ok. When I was talking about "better" versions, I only thought about some part of duplication. Other remade features are in fact better, without quotes (or equally good, or better in some ways but worse in others). It was also not my intention to start a completely unrelated discussion in this thread.
Last edited on
fun2code wrote:
Tests for self assignment crop up.
A few years ago, that was more common. Now, however, that should never happen to you - the copy-and-swap idiom is used instead as it is both efficient and guaranteed to never have the self-assignment problem.

@Mats: Abramus shares a view with FQA.

@htirwin: If something has many common uses, I don't consider it self-documenting. Pointers are not self-documenting because they can be sued in such a variety of ways that manual documentation is always required. The high level classes in the Standard Library are used for specific purposes and thus are self-documenting, because you know when you see one that it is always used the way you expect.

I also almost always look at a reference every time I use a standard library class, even ones I am intimately familiar with like std::vector. If you're not looking at the documentation for the class you're using while you're using it, then I think you're more likely to make a mistake or do something in an over complicated way.

@YellowPyramid: I addressed iterators in the article...?
Last edited on
@Mats: Abramus shares a view with FQA.
Some, yes. And...?
closed account (D80DSL3A)
@LB I shall study the newer language idioms more. Problem is, the language (pre c++11) was already so capable that one seems to rarely need other tools (though I admit that not knowing of the tools may influence this perception).

For fun (That's what we're here for right?) I thought I'd try creating a list with nodes in which a reference to the next node is used rather than a pointer. I got it to work! The key was creating a self referential node 1st for use as the list tail, then push_back() on this tail node.
Note the rather questionable ctor for the refNode class which creates self-referential nodes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
struct refNode
{
    int data;
    refNode& rNext;
    refNode(int Data): data(Data), rNext(*this) {}// self referential node to be used as list tail
    refNode(int Data, refNode& rRN ): data(Data), rNext(rRN) {}// "normal" ctor. reference is to next node inj list
};

// 2 functions for creating and displaying a refList
refNode& createList( std::istream& is, int& size );
void printList( refNode& rRN );

int main()
{
    std::ifstream fin("test.txt");
    if( !fin )// exception handling at its finest!!
    {
        cout << "bad istream \n";
        return 1;
    }

    int listSize = 0;
    refNode& rList = createList( fin, listSize );

    printList( rList );

    cout << endl;
    return 0;
}

refNode& createList( std::istream& is, int& size )
{
    int temp;
    refNode* pPrev = 0;// sorry about the pointer! I ouldn't think of how to avoid using one here.

    if( is >> temp )
    {
   //     refNode tail( temp );// oops. What was I thinking?
   //     pPrev = &tail;
        pPrev = new refNode( temp );// correct!
        size = 1;
        while( is >> temp )
        {
            pPrev = new refNode( temp, *pPrev );
            ++size;
        }
    }
    if( !pPrev ) cerr << "null ptr returning from createList() \n";// More fine exception handling (Let it crash, it will tell us why)
    return *pPrev;
}

void printList( refNode& rRN )
{
    refNode* pCurr = &rRN;// WARNING! pointer in use!

    while( pCurr != &(pCurr->rNext) )// require that node is not referencing self
    {
        cout << pCurr->data << " ";
        pCurr = &(pCurr->rNext);// point to next refNode
    }

    cout << " tail = " <<pCurr->data;
}

When I supply a test.txt with contents = 10 21 33 45
I get this output:
45 33 21  tail = 10

Indicating it works as expected.

I'm having trouble getting a destroyList() to work right at the moment though...

EDIT: Found the problem! Lines 38, 39 are very wrong (local refNode as list tail)!
New line 40 corrects this.
This destroyList() appears to work:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int destroyList( refNode& rRN )
{
    refNode* pCurr = &rRN;
    int count = 1;// CANNOT be 0

    while( pCurr != &(pCurr->rNext) )// require that node is not referencing self
    {
        cerr << pCurr->data << " ";
        refNode* pTemp = pCurr;// will be deleting this refNode
        pCurr = &(pCurr->rNext);// point to next refNode
        delete pTemp;
        ++count;
    }

    // the last, self referential one
    cerr << pCurr->data << " ";
    delete pCurr;// this one was counted initially
    return count;
}

Calling this adds the following to the program output:
45 33 21 10 releasing 4 refNodes from list


EDIT2: A push_front() function is easy enough. Assuming a 1st node already exists:
1
2
3
4
5
refNode& push_front( refNode& rHead, int Data, int& size )
{
    ++size;
    return *(new refNode( Data, rHead ) );
}

However, the list in the main() can't be added to. That would require re assigning rList. So, once again I resort to pointers:
1
2
3
4
5
6
7
8
9
10
11
int listSize = 0;
    refNode* pList = &createList( fin, listSize );

    printList( *pList ); cout << endl;// 45 33 21 10

    // add one more refNode to the list
    pList = &push_front( *pList, 99, listSize );// this line is why a pointer is needed.

    printList( *pList ); cout << endl;// 99 45 33 21 10

    cout << "releasing " << destroyList( *pList ) << " refNodes from list \n";

Output now is:
45 33 21  tail = 10
99 45 33 21  tail = 10
99 45 33 21 10 releasing 5 refNodes from list


OK. I promise to stop now.
errr... except to note that sizeof( refNode ) = 4 + sizeof( int ), so rNext is costing 4 bytes.
Last edited on
Pages: 12345