Which pointer type to use when we just want to point to an object.

Hi,

Consider the following implementation of "string iterator"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
      class iterator
      {
        private:
          string* str_;
          size_t index_;
        public:
          iterator(string* = nullptr, size_t = 0);
          iterator(const iterator&);
          ~iterator() noexcept;

          iterator& operator = (const iterator&);
          iterator& operator = (iterator&&) noexcept;
          bool operator != (const iterator&) const noexcept;
          bool operator == (const iterator&) const noexcept;
          iterator& operator ++ ();
          iterator& operator ++ (int);
          iterator& operator -- ();
          iterator& operator -- (int);
          char& operator * () const;
      };


Here, member variable "str_" is used to point to the string object to which the iterator is initialised.

[A] Aim : Eliminate raw pointer "str_" used in the above implementation.

Few observations :
[1] Since string is not allocated by iterator (not owner) rather it is just an object pointing to string object, "unique_ptr<string*> str_" doesn't look like a feasible solution.

[2] I am not very sure if using shared_ptr would be a good idea either as it is pretty expensive.

Kindly let me know the best way to achieve [A].

Thanks
Last edited on
How about a reference?
A reference would prevent the iterator from being reassigned. Personally I would just use a raw pointer.

Alternatives are std::reference_wrapper or observer_ptr.

https://en.cppreference.com/w/cpp/utility/functional/reference_wrapper
https://en.cppreference.com/w/cpp/experimental/observer_ptr
Thanks repeater for taking a look.

Repeater wrote:
How about a reference?


If we use reference, then how do we implement "operator =" ?

Here is how I have implemented it :

1
2
3
4
5
  iterator& iterator::operator = (const iterator& rhs) noexcept {
    str_ = rhs.str_;
    index_ = rhs.index_;
    return *this;
  }


Please let me know if we can use an alternative implementation for above operator?

Thanks



P.S. : Below is full iterator implementation :

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
64
65
66
67
68
69
70
iterator::iterator(string *str, size_t index)
    : str_{str}, index_{index} {
  }

  iterator::iterator(const iterator& itr)
    : str_{itr.str_}, index_{itr.index_} {
  }

  iterator::iterator(iterator&& rval) noexcept
    : str_{rval.str_}, index_{rval.index_} {
  }

  iterator::~iterator() noexcept {
    str_ = nullptr;
    index_ = 0;
  }



  iterator& iterator::operator = (const iterator& rhs) {
    str_ = rhs.str_;
    index_ = rhs.index_;
    return *this;
  }

  iterator& iterator::operator = (iterator&& rhs) noexcept {
    str_ = rhs.str_;
    index_ = rhs.index_;
    return *this;
  }

  bool iterator::operator != (const iterator& rhs) const noexcept {
    return (str_ != rhs.str_) || (index_ != rhs.index_); 
  }

  bool iterator::operator == (const iterator& rhs) const noexcept {
    return (str_ == rhs.str_) && (index_ == rhs.index_);
  }

  iterator& iterator::operator ++ () {
    ++index_;
    return *this;
  }

  iterator& iterator::operator ++ (int dummy) {
    ++(*this);
    return *this;
  }

  iterator& iterator::operator -- () {
    --index_;
    return *this;
  }

  iterator& iterator::operator -- (int dummy) {
    --(*this);
    return *this;
  }

  char& iterator::operator * () const {
    return (*str_)[index_];
  }

  iterator string::begin() {
    return iterator(this);
  }

  iterator string::end() {
    return iterator(this, sz_);
  }
Last edited on
If we use reference, then how do we implement "operator =" ?


You wouldn't. An iterator of this nature would apply to a single container (such as a string) and that would be all there would be to it. The iterator would be created for use with a single, existing container only.
Repeater wrote:

An iterator of this nature would apply to a single container (such as a string) and that would be all there would be to it.


But an iterator of this nature would not be same as the one provided by std::string.

For eg. the below code would then not work for our iterator while it works well for std::string
1
2
3
4
5
  std::string s1{"hello"};
  std::string s2{"world"};
  std::string::iterator it1 = s1.begin();
  std::string::iterator it2 = s2.begin();
  it1 = it2;   // [B] 


What if we want to achieve both [A] and [B] ([A] mentioned here : http://www.cplusplus.com/forum/general/248200/#msg1094007 )

Thanks
Last edited on
But an iterator of this nature would not be same as the one provided by std::string.


If you want an iterator that is the same as the one provided by std::string.... use the one provided by std::string.

You're writing your own because you want it to do something else, otherwise why would you be writing it. So what's different about yours?
kapil's iterator type has one big advantage over std::string::iterator in that it can handle the string data being reallocated, and it would also be relatively easy to implement bounds-checking if that is desirable.

If copying the iterator just copies each member as shown above there isn't really a need for you to define your own copy operations because the defaulted copy constructor and copy assignment operators would do the exact same thing. There is also no need to define move constructor or move assignment operator because there is no clever optimization that you can do in this case to make it move faster than a copy.
Last edited on
A standard library compatible iterator (for example, an iterator that can be used with standard algorithms) must be CopyAssignable
Repeater wrote:

If you want an iterator that is the same as the one provided by std::string.... use the one provided by std::string.

You're writing your own because you want it to do something else, otherwise why would you be writing it. So what's different about yours?

Apologies for putting the requirement in an improper way.

Suppose we have requirement like this :
[i] Implement string iterator without using any library provided iterators.
[ii] Usage of raw pointers is not allowed.
[iii] You should be able to do "it1 = it2" where it1 and it2 are iterators to different strings.

How can we modify iterator implementation here : http://www.cplusplus.com/forum/general/248200/#msg1094007
to meet above requirements.
Last edited on
Thanks a lot Peter87 and JLBorges for your inputs :)
kapil2905 wrote:

Suppose we have requirement like this :
[i] Implement string iterator without using any library provided iterators.
[ii] Usage of raw pointers is not allowed.
[iii] You should be able to do "it1 = it2" where it1 and it2 are iterators to different strings.

How can we modify iterator implementation here : http://www.cplusplus.com/forum/general/248200/#msg1094007
to meet above requirements.


Still looking for a solution to the above requirements.

Thanks
Also a raw pointer is also commonly known as a void* so you should ask your teacher if that is what you should avoid (but then again, people have their own interpretations).
Peter87 wrote:

A reference would prevent the iterator from being reassigned. Personally I would just use a raw pointer.

Alternatives are std::reference_wrapper or observer_ptr.

Just saw this post.

Thanks Peter87 for suggesting an alternative way.

I agree that a raw pointer seems to be the best option to implement an iterator which also seems to be inline with the following isocpp guideline : https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#f7-for-general-use-take-t-or-t-arguments-rather-than-smart-pointers
Thanks poteto for your suggestion :)
Topic archived. No new replies allowed.