Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

error: request for member ... which is of non-class type

Tags:

c++

I realize the error is coming from using vectors in a custom class, but I have been struggling how to fix them. How do I call vector methods when its part of the class object?

These are the errors I am getting:

Word.cpp: In member function ‘void Word::addPosition(int)’:
Word.cpp:20: error: request for member ‘push_back’ in ‘((Word*)this)->Word::positions’, which is of non-class type ‘std::vector<int, std::allocator<int> >*’
Word.cpp: In member function ‘int Word::getPosition(int)’:
Word.cpp:26: error: request for member ‘size’ in ‘((Word*)this)->Word::positions’, which is of non-class type ‘std::vector<int, std::allocator<int> >*’
Word.cpp:27: error: request for member ‘size’ in ‘((Word*)this)->Word::positions’, which is of non-class type ‘std::vector<int, std::allocator<int> >*’
Word.cpp:29: error: cannot convert ‘std::vector<int, std::allocator<int> >’ to ‘int’ in return

Header

#pragma once
#include <string>
#include <vector>

class Word {
  public:
    Word();
    ~Word();
    void setWord(std::string);
    void addPosition(int);
    std::string getWord();
    int getPosition(int);
  private:
    std::string word;
    std::vector<int> *positions;
};

Implementation

#include "Word.h"
#include <string>
#include <vector>

Word::Word() {
    this->word = "";
    this->positions = new std::vector<int>(5);
}

void Word::setWord(std::string s) {
    this->word = s;
}

void Word::addPosition(int i) {
    this->positions.push_back(i);
}

std::string Word::getWord() {
    return this->word;
}

int Word::getPosition(int i) {
    if (i < this->positions.size() && i > 0) {
        for (int j = 0; j < this->positions.size(); i++) {
            if (i == j) {
                return positions[j];
            }
        }
    }
    return -1;
}

edit: Is this a better way to set up the class? header:

    #pragma once
    #include <string>
    #include <vector>

    class Word {
      public:
        Word();
        ~Word();
        void setWord(std::string);
        void addPosition(int);
        std::string getWord();
        int getPosition(int);
      private:
        std::string word;
        std::vector<int> positions;
    };

implementation:

Word::Word(){
    word = "";
}

void Word::setWord(std::string s){
    this -> word = s;
}
void Word::addPosition(int i){
    this -> positions.push_back(i);
}
std::string Word::getWord(){
    return this -> word;
}
int Word::getPosition(int i){
    if (i < this -> positions.size() && i > 0) {
        for (int j = 0; j<this -> positions.size(); i++) {
            if (i == j) {
                return (this->positions)[j];
            }
        }
    }
    return -1;
}

but now I am getting this error:

Undefined symbols for architecture x86_64:
  "_main", referenced from:
      start in crt1.10.6.o
ld: symbol(s) not found for architecture x86_64
collect2: ld returned 1 exit status
like image 910
badger0053 Avatar asked Sep 14 '13 20:09

badger0053


3 Answers

In addition to @Chris answer, the de facto problem here is that you are using a pointer to a vector instead of a vector directly.
There is no reason to allocate dynamically a vector, because it internally uses dynamic memory to store its elements. The advantage of vector is that it manages its own memory, and that memory its released at the end of its scope: Thats RAII.

Personally I recommend the Going Native 2013 Stroupstrup talk "The Essence of C++". He provides pretty examples and explanations about data management in C++: Data vs Handlers of data.

For those coming from Java: A perspective about memory management in Java and C++

In Java we differentiate two kinds of data: Basic types (Ints, chars, booleans, etc), and objects (Instances of classes). Java manages its memory through a Garbage Collector: The Java runtime (The JVM) allocates the memory used by objects on the heap, and what provides you is a pointer to that allocated object. So in Java your variables are really pointers allocated on the stack pointing to the heap memory where the object is allocated (This is directly related with the usual problems which noobs have with Java pass by value). This allows Java to share ownership of object instances easily: It only tracks (via reference counting) how many variables refers to an specific object.
Occasionally the garbage collector look at the program heap and releases the memory used by objects which reference count have dropped to zero (That is, objects which you are not using yet). So, as you can see, The GC provides a non-deterministic way to release/manage the memory.

C++ memory management is dessigned to work in a deterministic way. All variables are declared in a specific scope, which determines its lifetime When the scope of a variable ends, the lifetime of that variable ends, and in the case of objects, its destructor its called to execute the apropiated releasing operations.
Thats known as Resource Acquisition Is Initialization, or RAII.

RAII means that objects manage resources (Memory, file handles, network ports, etc), and the adquisition, use, and release of that resource is directly linked to the lifetime of the object which manages the resource. This provides a deterministic and leak-free system to manage resources: Note that "resources" don't refers to memory only, which is the only resource which a Garbage Collector manages. RAII is, in the 99% of the cases, much more powerfull and secure than a garbage collector.

std::vector is the tipical example of a class that manages a resource (A dynamically allocated resizable array in its case) through RAII.
As you can see, I havent talk about pointers at all: Pointers have to be used to share ownership of an object, or to allocate a dynamic chunk (Array) of memory. But note that the first case could be (And must be) achieved through smart pointers, and the second through RAII-based containers such as std::vector.

The problem with your use of a pointer to dynamically-allocate a std::vector are:

  • std::vector manages internally a dynamic array where it allocates the elements, so std::vector size (That is, what vector fits in the stack) its only the size of the pointer pointing to the array, and two counters (Integers). Don't worry about the size of vector in the stack (Due to possible stackoverflows): The array is allocated on the heap.

  • Using a pointer to allocate dynamically a vector breaks RAII, because the lifetime of the vector instance is not linked to any scope, its lifetime must be determined by you, when you decide to deallocate (delete) the vector. Of course manual dynamic memory management its error prone.

Again, I recommend you to view Stroupstrup's talk: He explains this much better than me :)

like image 139
Manu343726 Avatar answered Oct 15 '22 03:10

Manu343726


positions is a pointer. You need to dereference it in order to get to the base vector:

this->positions->size();

In general, the error you're getting is caused by failing to dereference (because T* is not a class type; it's a pointer, and pointers are a primitive type).

like image 4
Chris Hayes Avatar answered Oct 15 '22 04:10

Chris Hayes


positions is a pointer, so you need to use indirection:

this->positions->push_back(i);

Don't use pointers in this situation. You're better off using a stack-allocated vector and initializing it through the constructor:

class Word
{
    ...
private:
    std::string word;
    std::vector<int> positions;
};

Word::Word()
    : word(""), positions(5) { }

void Word::addPosition(int i)
{
    this->positions.push_back(i);
}
like image 2
David G Avatar answered Oct 15 '22 04:10

David G