Vector or C-style array?

Still learning and have a long ways to go, so all feedback welcome. I've seen it written all over the internet that vectors are 'always' a better solution than arrays, but I'm not sure 'why' in this case.

The personal project I'm working on requires the translation of human readable music theory to a pure mathematical system, and back to human readable music theory.

The easiest way I've found to implement this incorporates several arrays whose values and sizes would always be constant, and would store only primitive data types.

The largest array of this kind I would need would consist only of 12 elements all storing chars.

I know vectors are preferred largely because of how they deal with memory management behind the scenes (though granted, I don't really know in detail 'why/how' this is the case), but for a collection of this type, do vectors even lend that benefit? Are there other benefits I'm not aware of?

There are a few operations I would be doing frequently such as performing basic modulus operations and ensuring that while iterating through the collection, the iteration would continue past the last element by going back to the first (or going to an element based on some mod operation). Would a vector provide better ways of doing this?

I've considered other data structures (such as a circular array), but everything else doesn't seem to simplify my needs all that much, while adding extra overhead.

I had other questions about the use of chars but... it appears I've written a novel haha, so I'll leave it at this for now. But thanks in advance to anyone who takes the time to read and/or respond to this!
> The easiest way I've found to implement this incorporates several arrays whose values and sizes would always be constant
> The largest array of this kind I would need would consist only of 12 elements all storing chars.

An array is perfectly fine if the size is a constant known at compile time.

However, strongly favour the c-style array with a college education: std::array<>
http://en.cppreference.com/w/cpp/container/array
Thanks for the reply JLBorges! Glad my thinking wasn't too far off. Though I'm not sure how I completely forgot about the std::array<> (if I ever knew about it). I can't really remember it being discussed by teachers in any of my classes, but a single google search, as well as your link, makes me question why it hasn't been emphasized more. It definitely re-affirms my feeling that I need to be doing far more on my own rather than wait to be taught.
The reason to use std::array and std::vector is the use of STL. It would not require from you a lot of effort for memory management , etc. STL have a lot of algorithms and compatible iterators to iterate safely.

Vector are dynamic array . Array is the fastest data structure , in most case.

Alligned data is super important and fast .

Create a class that provides another layer to the data , with a method or two to do the circular thing is enough .

But I wonder why you would need to go back to the beginning , instead of simply use local iterators at each frame to iterate .

Thanks Ericool! I just did some additional research into the STL library. I didn't realize or consider the optimization provided by STL in processing the stored data. So thank you, that cleared some things up for me.

Concerning the question regarding local iterators; I actually didn't realize I had so many options with how I iterate until your question prompted me to do another google search. Originally I was going to iterate using a simple for loop or while loop.

After glancing over some material on iterators just now, I'm still not sure how I could take advantage of them. I have 5 constant arrays at the moment. The array of size 12 contains all the notes on a piano keyboard represented by chars for the note's letter name, or in the case of accidentals (black notes), an 'X' (where the program will determine if that 'X' is a sharp or flat). The other arrays store int values that follow the stepwise formula for building standard scales.

So when a key and scale type has been selected, the appropriate scale needs to be constructed using the above arrays. If the key selected is 'B' (whose index is 11) and the major scale is selected, the first note of the new B scale array is B. The following note, defined by the Major_Scale_Array, is 2 steps after B. So the program needs to loop back to the first element of the Piano_Keyboard_Array, followed by one more step to give us the note C#.

So I guess my question is, how would I use local iterators to provide that specific functionality?
Last edited on
what you are saying is this :

1
2
const char * letterName[12];
"other array" [12];


what you should do instead is :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <array>

class PianoKeyNote
{
  const char * name;
  bool isBlank;
  bool isSharp;
  int scale;
//etc
};

std::array < PianoKeyNote ,12 > notes; //12 notes

//iterating through all notes
auto current_it = notes.begin();
const auto last_it = notes.end();

while(current_it != last_it)
{
     ( * current_it ++ ).name;
//or instead 
   ( * current_it ) . name;
   ++ current_it;
}

> The reason to use std::array and std::vector is the use of STL.

No. The standard library iterators and algorithms (STL is a misnomer) work equally well on c-style arrays: a raw pointer is a random access iterator.

The primary advantages of std::array<> over a c-style array are that it knows its own size, that it is CopyAssignable, and that it supports lexicographical comparisons using standard operators ( a <= b ).


> Originally I was going to iterate using a simple for loop or while loop.

That would still be, by far, the best approach.
Strongly favour range-based loops over classical loops. http://www.stroustrup.com/C++11FAQ.html#for

Something along these lines, perhaps:

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
#include <iostream>
#include <array>
#include <algorithm>

// ordered as in the standard chromatic scale. (AS_BF, FS_GF etc. are the semitones)
enum note { A, AS_BF, B, C, CS_DF, D, DS_EF, E, F, FS_GF, G, GS_AF };

std::ostream& operator<< ( std::ostream& stm, note n )
{
    static const char* const name_of[]
    { "A", "AS_BF", "B", "C", "CS_BF", "D", "DS_EF", "E", "F", "FS_GF", "G", "GS_AF" } ;
    return stm << name_of[n] ;
}

template < std::size_t N > using scale = std::array<note,N> ;

template < std::size_t N > std::ostream& operator<< ( std::ostream& stm, const scale<N>& notes )
{
    // http://www.stroustrup.com/C++11FAQ.html#for
    for( note n : notes ) stm << n << ' ' ;
    return stm ;
}

using chromatic_scale = scale<12> ;

constexpr chromatic_scale standard_chromatic_scale // extra pair of braces to suppress a silly warning
{ { A, AS_BF, B, C, CS_DF, D, DS_EF, E, F, FS_GF, G, GS_AF } } ; 

chromatic_scale keyed_chromatic_scale( note n )
{
    chromatic_scale cs = standard_chromatic_scale ;
    // http://en.cppreference.com/w/cpp/algorithm/rotate
    std::rotate( std::begin(cs), std::begin(cs) + n, std::end(cs) ) ;
    return cs ;
}

using standard_scale = scale<7> ;
using intervals = std::array<int,7> ;

// 2: tone interval, 1:semitone interval
constexpr intervals major_scale_intervals { { 2, 2, 1, 2, 2, 2 } } ;

standard_scale generate_scale( const chromatic_scale& cs, const intervals& steps )
{
    standard_scale result {};

    std::size_t pos = 0 ;
    for( std::size_t i = 0 ; i < result.size() ; ++i )
    {
        result[i] = cs[ pos % cs.size() ] ; // *** note: modulo 
        pos += steps[i] ;
    }

    return result ;
}

standard_scale major_scale( note key ) 
{ return generate_scale( keyed_chromatic_scale(key), major_scale_intervals ) ; }

int main ()
{
    for( note key : standard_chromatic_scale )
        std::cout << key << " major: " << major_scale(key) << '\n' ;
}

http://coliru.stacked-crooked.com/a/ec279ce9911a910e
Last edited on
Thanks again guys, seriously. I'm in awe of just how much I've learned in what amounts to a few posts.

Ericool: Thanks for clarifying your approach. At the very least, it gave me a clear and concise look at how iterators can be used, and what they are.

JLBorges: Your thorough example here (I'm not sure if you wrote this or dug it up from somewhere, but either way) was far more than I could have asked or hoped for. Since posting my question, I've updated my program and narrowed down a better design scheme. This code confirms that I'm headed in the right direction for my design, but also seems to provide a lot of useful approaches to solving some problems that I didn't know about.

After studying the code provided, I feel like I understand pretty much what's going on except in the case of:
1
2
3
4
5
6
std::ostream& operator<< ( std::ostream& stm, note n )
{
    static const char* const name_of[]
    { "A", "AS_BF", "B", "C", "CS_BF", "D", "DS_EF", "E", "F", "FS_GF", "G", "GS_AF" } ;
    return stm << name_of[n] ;
}


Googling it provided some insight, but the structure and process is still confusing. So you have
std::ostream& operator<< ( std::ostream& stm, note n )
followed by brackets. I'm a bit confused as to what I'm looking at. Clearly it's not describing a class, and it doesn't appear to be describing a standard function, which leads me to think it's a c++ operation similar to an if statement or a loop. The purpose seems to be to translate the note enum values to strings, and possibly vice versa... ... which incidentally is something I need to do with my enums, so if this is the best approach for that, understanding it would be helpful ha.
Last edited on
you should see this as a normal method which returns a stream (object) , then this stream is output to the screen . Depending on the type send to this operator << (method) it returns something different . You should at std::cout in the code , that is normally where these operator overloaded method are called .
> and it doesn't appear to be describing a standard function

It is a function with a special name operator<< It is an overloaded stream insertion operator.
http://en.cppreference.com/w/cpp/language/operators

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
#include <iostream>
#include <string>

enum note { A, AS_BF, B, C, CS_DF, D, DS_EF, E, F, FS_GF, G, GS_AF };

std::ostream& operator<< ( std::ostream& stm, note n )
{
    static const char* const name_of[]
    { "A", "AS_BF", "B", "C", "CS_BF", "D", "DS_EF", "E", "F", "FS_GF", "G", "GS_AF" } ;
    return stm << name_of[n] ;
}

int main()
{
    const std::string str = "hello world!\n" ;

    std::operator<< ( std::cout, str ) ;

    std::cout << str ; // same as above

    operator<< ( std::cout, str ) ; // same as above; Koenig lookup
    
    

    const note Re = D ;

    std::cout << Re << '\n' ;

    operator<< ( std::cout, Re ) << '\n' ; // same as above

    std::operator<< ( operator<< ( std::cout, Re ), '\n' ) ; // same as above
}

http://coliru.stacked-crooked.com/a/2af50167b7bbba9c
Last edited on
Topic archived. No new replies allowed.