Method in a class calling upon method in encapsulated class variable

Sorry for the confusing title; as a newbie I haven't learned much of the jargon.
Okay, here's the simplified structure of my problem.

I apologize in beforehand for the long post, and my unclear language (it is 1 AM)



The return values of some methods are like 1718968935, 20092525.... etc, even though I have initialized all variables before calling those methods.

(I am including the full code at the end of this post).

(Note, the necessary #include and namespace std; are are omitted for readability)

We have a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A {      //I have tested class A separately, and everything works fine)
   double * buffer;  //pointer to array of doubles
   int elements;     //number of elements in buffer (since we can 'remove' elements from the buffer, the number of elements will not always be the same as the size of buffer.)
   ...               //other variables
   public:
      A(int size);  // constructor
      ...              //methods that add/remove elements in buffer, and manipulate or return the value of other variables

};

A :: A(int size) {
buffer = new double[size];
... // buffer is initialized with 0:s, and all other variables are initiated 
}

...  //Other methods are defined, including methods that replaces elements in the buffer 


Now comes class B (in which a variable is of class A)
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
class B{
   A * buffer_element;
   ...   // other variables
   public:
      B(double array[], int size); // creates the *buffer_element
      ... // other methods that manipulate *buffer_element and other variables
};

B :: B(double array[], int size) {
buffer_element = new buffer(size);
... // adds elements from array[] into buffer USING methods for class A.
}

... // other methods

//added a test code into main:
int main(){
    double thing[] = {.....}; //array is created
    int size = ...;
    B b_variable = B(thing, size);
     // a B class is created, and the buffer_element (b_variable->buffer_element) is created too.
// NOTE: When I debugged, this part worked fine. (See details far below)

//Now I try to return the variable "elements" of the buffer_element. Since I am debugging I made all variables public for this to work

cout << b_variable->buffer_element->elements;
// This gives me a horrible output of things like "1718968935"

... // Other method calls and outputs for debugging principles fail too. 


The thing is that creating the buffer_element goes smoothly (I had put cout<< expressions a bit here and there in the methods for class A, and they gave correct outputs), however, as soon as I tried using a method from B to extract info from the class A variable created withing class B, i get these weird numbers

Any advice?



My code is below

We had an assignment at school to make a Java implementation of the Ringbuffer class , and a Guitarstring class (and a main() that allowed us to play music), but i am now trying to create a C++ code (merely for practice; not some assignment). Those who are interested can read about the assignment here: http://tinyurl.com/2asmxew


Header file for class A: Ringbuffer class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifndef _RINGBUFFER_H_
#define _RINGBUFFER_H_

using namespace std;

class Ringbuffer {
    public: //Made public for debugging purposes only
    int capacity; //how big the buffer can be
    int elements; //number of elements
    double * buffer; //the buffer in question
    int first; //ringbuffer starts at this index
    int last; //buffer ends at this index: There may be a loop around
    public:
        Ringbuffer(int cap);
        int size() const; //return number of elements currently in buffer
        bool isEmpty() const; //is buffer empty (size == 0);
        bool isFull() const; //is buffer full (size == capacity);
        void enqueue(double); //add item x to the end of the ringbuffer;
        double dequeue(); //delete and return item to front
        double peek() const; //return (but not delete) item from front
    };

#endif 


Ringbuffer.cpp file

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
#include <iostream>
#include <string>
#include "Ringbuffer.h"

using namespace std;

Ringbuffer :: Ringbuffer(int cap) {
    first = 0;
    last = 0;
    elements = 0;
    capacity = cap;
    buffer = new double [cap];
    for (int i =0; i<cap; i++) {
        buffer[i] = 0;
    }
}
int Ringbuffer::size() const {
    return elements;
}

bool Ringbuffer::isEmpty() const {
    return (size() == 0);
} 

bool Ringbuffer::isFull() const {
    return (size() == capacity);
}

void Ringbuffer::enqueue(double x) {
    if(!isFull()) {
        buffer[last] = x;
        elements++;
        last = (last + 1)%capacity;
    }
    else {cout << "Can't add more" << endl;}
}

double Ringbuffer::dequeue() {
    if(!isEmpty()) {
        double num = buffer[first];
        buffer[first] = 0;
        first = (first + 1)%capacity;
        elements--;
        return num;
    }
    else return 0;
}

double Ringbuffer :: peek() const {
    return buffer[first];
}



Class B: The Guitarstring class

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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <iostream> 
#include <cmath>
#include "Ringbuffer.h"

using namespace std;

#define Sampling_Rate 44100
#define decay 0.996

class GuitarString {
    public: // only for debugging purposes
    int tics;
    int size;
    Ringbuffer * ringbuffer;
    public:
        GuitarString(double frequency); // create guitar string of given frequency
        GuitarString(double init[], int s); // create guitar string whose size and initial values are given by the array
        void pluck(); // set the buffer to white noise
        void tic(); // advance simulation one step
        double sample(); // return the current sample
        int time(); // return number of tics
};

GuitarString :: GuitarString(double frequency) {
    int capacity = (int) round(Sampling_Rate/frequency);
    Ringbuffer * ringbuffer = new Ringbuffer(capacity);
    for (int i = 0; i < capacity; i++) {
        ringbuffer->enqueue(0);
    }
    size = capacity;
    tics = 0;
}

GuitarString :: GuitarString(double init[], int s) {
    Ringbuffer * ringbuffer = new Ringbuffer(s);
    size = s;
    for (int i = 0; i < s; i++){
        ringbuffer->enqueue(init[i]);
    }
    tics = 0;
}

void GuitarString::pluck() {
    for (int i = 0; i < size; i++){
        int a = rand();
        double b = (a - 0.5*RAND_MAX)/RAND_MAX;
        ringbuffer->dequeue();
        ringbuffer->enqueue(b);
    }
}

void GuitarString::tic() {
    double a = ringbuffer->dequeue();
    double b = ringbuffer->peek();
    double c = decay*0.5*(a + b);
    ringbuffer->enqueue(c);
    tics++;
}

double GuitarString::sample() {
    return (ringbuffer->peek());
}

int GuitarString::time(){
    return tics;
}

int main(void) {
      int N;
      cout << "Insert capacity: ";
      cin >> N; // For example 10.
      double init[N];
      for (int i = 0; i < N; i++) {
          init[i]=i*2;
      }
      
      GuitarString str = GuitarString(init, N);

       //cout << str.ringbuffer->first << endl;
       // crashes the .exe file for this particular code; for a slightly different code it gives the weird number.
       // I try to keep track of the variables in class Ringbuffer, and it is only when I call from my GuitarString class that those variables suddenly shoot off.

        //cout << str.sample();
        // The above line also crashes the .exe file
      
      return 0;
}


Thank you for your help!
looks like in both your GuitarString constructors - you are creating a local variable
of type Ringbuffer * ringbuffer which will go out of scope at the end of the constructor.
You should be using the actual class member variable.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
GuitarString :: GuitarString(double frequency) {
    int capacity = (int) round(Sampling_Rate/frequency);
    Ringbuffer * ringbuffer = new Ringbuffer(capacity); //ERROR = local variable Ringbuffer * ringbuffer being used
    for (int i = 0; i < capacity; i++) {
        ringbuffer->enqueue(0);
    }
    size = capacity;
    tics = 0;
}

GuitarString :: GuitarString(double init[], int s) {
    Ringbuffer * ringbuffer = new Ringbuffer(s);//ERROR = local variable Ringbuffer * ringbuffer being used
    size = s;
    for (int i = 0; i < s; i++){
        ringbuffer->enqueue(init[i]);
    }
    tics = 0;
}


You should be using this variable:

1
2
3
4
5
6
7
8
class GuitarString {
    public: // only for debugging purposes
    int tics;
    int size;
    Ringbuffer * ringbuffer;//This one here
    public:
        GuitarString(double frequency); // create guitar string of given frequency
   

So I just changed to
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
GuitarString :: GuitarString(double frequency) : ringbuffer( new Ringbuffer( (int) round(Sampling_Rate/frequency))) {
    int capacity = (int) round(Sampling_Rate/frequency);
    for (int i = 0; i < capacity; i++) {
        ringbuffer->enqueue(0);
    }
    size = capacity;
    tics = 0;
}

GuitarString :: GuitarString(double init[], int s): ringbuffer( new Ringbuffer( s)) {
    size = s;
    for (int i = 0; i < s; i++){
        ringbuffer->enqueue(init[i]);
    }
    tics = 0;
}


and it seems to work now (at least the second constructor; haven't tested the first one yet).

Thank you so much!

A follow up question though
Is there a neater way than to write ": ringbuffer( new Ringbuffer( s))" in the Constructor call?

For example: :
1
2
3
4
5
6
7
8
GuitarString :: GuitarString(double init[], int s){
    size = s;
    *ringbuffer = Ringbuffer(s);
    for (int i = 0; i < s; i++){
        ringbuffer->enqueue(init[i]);
    }
    tics = 0;
}


It seems to work, is it valid?

Once again, thank you so much!
Last edited on
Given that you are allocating a Ringbuffer each time you construct a GuitarString, the ringbuffer member variable could be a 'Ringbuffer' instead of a 'Ringbuffer *'. If this were the case, the code would be slightly simplified to:

 
GuitarString :: GuitarString(double frequency) : ringbuffer( (int) round(Sampling_Rate/frequency))


and you wouldn't have to de-allocate the memory in the destructor (which I think you may have forgotten to do in the above example).

--Rollie

Edit: Missed the latter part of your question above - No that is not valid, because you've never allocated memory on the heap for your 'ringbuffer' pointer, so you shouldn't be trying to dereference it.
Last edited on
Ah, you are entirely right.
When I tried the latter part i forgot to compile, and instead ran the functioning code.
Well I have a long way to go in C++.

Thanks for you help rollie!
Topic archived. No new replies allowed.