c++ 对象正在创建同一个数组的两个实例,但在不同的范围内?

Posted

技术标签:

【中文标题】c++ 对象正在创建同一个数组的两个实例,但在不同的范围内?【英文标题】:c++ Object is creating two instances of the same array, but in different scopes? 【发布时间】:2013-01-15 20:13:36 【问题描述】:

我的一门课程有问题。该类中只有 1 个 array 成员。我正在构建此类的静态对象,并初始化函数中的值。问题是永远不会插入值。

当我进入调试器并查看该数组中的一些基本插入语句时,该数组仍然为空。但是,如果我进入插入函数本身,我可以看到一个完全相同名称的“第二个”数组,并按预期存储值。

在我看来,好像有一个静态的外部范围数组,其中没有任何内容,还有一个正确存储内容的第二个内部版本(完全相同的数组)。

我在这里缺少什么吗?我真的不知道为什么会这样。

这是最低限度的源代码,根据要求

循环缓冲区.hpp

#ifndef __ma__circularbuffer_guard
#define __ma__circularbuffer_guard


#include <array>

template < typename T, int SIZE> 
class CircularBuffer

private:
int _index;
int _size;
std::array<T, SIZE> _buffer;
public:
CircularBuffer()  _index = 0; _size = SIZE; 

int             length  ();
typename T&     at      (int);
void            insert  (T);
int             index   ();

private:
int     realign (int&);


;

template < typename T, int SIZE> 
int CircularBuffer<T, SIZE>::realign (int& index)

if (index >= _size)

    index -= _size;
    realign(index);
 else if (index < 0)

    index += _size;
    realign(index);

return index;



template < typename T, int SIZE> 
int CircularBuffer<T, SIZE>::length ()

return _size;



template < typename T, int SIZE> 
typename T& CircularBuffer<T, SIZE>::at (int index)

realign(index);
return _buffer.at(index);



template <typename T, int SIZE>
void CircularBuffer<T, SIZE>::insert (T data)

realign(_index);
_buffer.at(_index) = data;
_index += 1;



template <typename T, int SIZE>
int CircularBuffer<T, SIZE>::index ()

return _index;


#endif

全局缓冲区初始化器

#ifndef __guard__namespace__notes__
#define __guard__namespace__notes__

#include "circularbuffer.hpp"
#include <memory>

typedef CircularBuffer<char, 7> CB_Natural_T;
typedef CircularBuffer<int, 12> CB_Chromatic_T;

static CB_Natural_T WHITENOTES = CB_Natural_T();        // buffer of letter notes
static CB_Chromatic_T POSITIONS = CB_Chromatic_T();     // buffer of absolute positions on keyboard

struct Initialize

    Initialize()
    
        WHITENOTES.insert('C');
        WHITENOTES.insert('D');
        WHITENOTES.insert('E');
        WHITENOTES.insert('F');
        WHITENOTES.insert('G');
        WHITENOTES.insert('A');
        WHITENOTES.insert('B');

        // Initialize all positions
        for (int i = 0; i < 12; ++i)
            POSITIONS.insert(i);
    
;

static Initialize dummy_init_var = Initialize();

#endif

初始化静态缓冲区,以便我可以对其他类进行单元测试。

注意类头和cpp

#ifndef __guard__note__
#define __guard__note__


#include "macros.h"
#include <string>
#include <memory>


class Note

public:
enum Qualities  UNKNOWN = -3, DFLAT, FLAT, NATURAL, SHARP, DSHARP ;   // qualities of note
typedef DEF_PTR(Note);                                                  // pointer type
private:
char _letter [1];       // the letter of the note
std::string _name;      // the full name of the note    
int _value;             // absolute value
int _position;          // relative position
Qualities _quality;     // sharp/natural/flat quality

public:
Note();
Note(char);             // letter
Note(char, Qualities);  // letter, and quality

// setters
void sharp();       // Sets the quality of the note to 1
void Dsharp();      // Sets the quality of the note to 2
void flat();        // Sets the quality of the note to -1
void Dflat();       // Sets the quality of the note to -2
void natural();     // Sets the quality of the note to 0

// getters
char letter() const;        /* returns character letter */
std::string name() const;   /* returns true name of note */
int position() const;       /* returns relative position on keyboard */
int quality() const;        /* returns the quality of the note */
void respell() const;       /* respells a note to the nearest other note */

static pointer_type make(char);             // returns a shared pointer of a new Note
static pointer_type make(char, Qualities);  // returns a shared pointer of a new Note

// operators
bool operator ==(Note& r) const;    // Returns true if Notes are truly equal
bool operator !=(Note& r) const;    // Returns true if Notes are truly not equal

bool isEnharmonic(Note& r) const;   // Returns true if Notes are enharmonically equal
bool isNatural() const;             // Returns true if Note is natural
bool isSharp() const;               // Returns true if Note is sharp
bool isDSharp() const;              // Returns true if Note is double sharp
bool isFlat() const;                // Returns true if Note is flat
bool isDFlat() const;               // Returns true if Note is double flat

private:
void makeName();    /* sets name of Note */
;


#endif


#include "note.h"

Note::Note() 

_letter[1] = 'u';
_name = ""; 
_value = -1; 
_quality = UNKNOWN;
_position = -1;


Note::Note(char l)

_letter[1] = l;

// determine absolute value based on letter
switch (l)

case 'C':
    _value = 0; break;
case 'D':
    _value = 2; break;
case 'E':
    _value = 4; break;
case 'F':
    _value = 5; break;
case 'G':
    _value = 7; break;
case 'A':
    _value = 9; break;
case 'B':
    _value = 11; break;
default:
    _value = -1; break;


_quality = NATURAL;
_position = _value + _quality;
makeName();


Note::Note(char l, Note::Qualities q)

_letter[1] = l;

// determine absolute value based on letter given
switch (l)

case 'C':
    _value = 0; break;
case 'D':
    _value = 2; break;
case 'E':
    _value = 4; break;
case 'F':
    _value = 5; break;
case 'G':
    _value = 7; break;
case 'A':
    _value = 9; break;
case 'B':
    _value = 11; break;
default:
    _value = -1; break;


_quality = q;       // assert for good data
_position = _value + _quality;
makeName();


void Note::sharp()       _quality = SHARP;     _position = _value + 1; makeName();
void Note::Dsharp()      _quality = DSHARP;    _position = _value + 2; makeName();
void Note::flat()        _quality = FLAT;      _position = _value - 1; makeName();
void Note::Dflat()       _quality = DFLAT;     _position = _value - 2; makeName();
void Note::natural()     _quality = NATURAL;   _position = _value;     makeName(); 

char Note::letter() const        return _letter[1]; 
std::string Note::name() const   return _name; 
int Note::position() const       return _position; 
int Note::quality () const       return _quality; 


Note::pointer_type Note::make(char l)                     return pointer_type(new Note(l)); 
Note::pointer_type Note::make(char l, Note::Qualities q)  return pointer_type(new Note(l, q)); 

void Note::makeName()

_name = "";
_name += _letter[1];    // add letter to name

// find out quality, add quality to name
switch (_quality)

case DFLAT:
    _name += "bb"; break;
case FLAT:
    _name += "b"; break;
case SHARP:
    _name += "#"; break;
case DSHARP:
    _name += "x"; break;
case NATURAL:
    break;
default:
    _name += "u"; break;



bool Note::operator ==(Note& r) const

// true if letter, value, position, and quality are all equal
return (_letter[1] == r._letter[1]) && (_value == r._value) && (_position == r._position) && (_quality == r._quality);


bool Note::operator !=(Note& r) const

return !(*this == r);


bool Note::isEnharmonic (Note& r) const

return (_position == r._position);


bool Note::isNatural() const

return _quality == NATURAL;


bool Note::isSharp() const

return _quality == SHARP;


bool Note::isDSharp() const

return _quality == DSHARP;


bool Note::isFlat() const

return _quality == FLAT;


bool Note::isDFlat() const

return _quality == DFLAT;

我也会发布间隔,但那个非常大。但基本上在称为 findInterval 的 Intervals 函数之一中有这段代码

间隔::findInterval

void Interval::findInterval(Note& bottom, Note& top)

int index = 0;      // temp placeholder for start position

// find where the bottom note is in relation to buffer
for (int i = 0; i < WHITENOTES.length(); ++i)

    if (bottom.letter() == WHITENOTES.at(i))
    
        index = i;  // set start position to this position
        break;
    


// find the interpreted interval
// starting from index, with offset of length + index
for (int i = index; i < (index + WHITENOTES.length()); ++i)

    if (top.letter() == WHITENOTES.at(i))
    
        _interval = i - index;  // set interval
        break;
    


// modify index to serve as the position of the bottom note
index = bottom.position();

// find the physical distance
for (int i = index; i < (index + POSITIONS.length()); ++i)

    if (top.position() == POSITIONS.at(i))      // values match
    
        _distance = i - index;                  // set physical distance
        break;
    
    else if (top.position() > 11 && ((top.position() - 11) == POSITIONS.at(i)))     // if top position is higher than octave
    
        _distance = (i - index) + 11;
        break;
    




这里设置数据成员失败,因为 WHITENOTES 是空的,即使我调用了静态结构来初始化它。

需要注意的另一件事是,如果我编译了我的 ut_interval,所有测试都完美返回,没有失败,当我在调试器中检查缓冲区的值时,它们显示为 \0。但是它仍然通过 if 语句并将字符与字母匹配(这是调试器中对字符的某种加密吗?)

但是,ut_chord 中的#includes 完全相同,它无法评估音程

这是音程 ut 和和弦 ut 的示例

ut_interval

#include "../common/namespace_notes.h"
#include "../common/note.h"
#include "../common/interval.h"

#define BOOST_TEST_MODULE IntervalTest
#include <boost/test/auto_unit_test.hpp>



#define TEST_IVL(i, dist, itv, q, n) \
BOOST_CHECK(i.distance() == dist); \
BOOST_CHECK(i.interval() == i.itv); \
BOOST_CHECK(i.quality() == i.q); \
BOOST_CHECK(i.name() == n)



BOOST_AUTO_TEST_CASE(INTERVAL_UNISONS)

// make some notes
Note C = Note('C');
Note Cs = Note('C', Cs.SHARP);
Note Cds = Note('C', Cds.DSHARP);
Note Cf = Note('C', Cf.FLAT);
Note Cdf = Note('C', Cdf.DFLAT);

// make some intervals
Interval PUnison = Interval(C, C);
Interval AugUnison = Interval(C, Cs);
Interval Aug2Unison = Interval(C, Cds);
Interval DimUnison = Interval(C, Cf);
Interval Dim2Unison = Interval(C, Cdf);

// make sure members are accurate
TEST_IVL(PUnison, 0, UNISON, PER, "Perfect Unison");
BOOST_CHECK(PUnison.isPerfect());

TEST_IVL(AugUnison, 1, UNISON, AUG, "Augmented Unison");
BOOST_CHECK(AugUnison.isAugmented());

TEST_IVL(Aug2Unison, 2, UNISON, AUG, "Augmented Unison");
BOOST_CHECK(AugUnison.isAugmented());

TEST_IVL(DimUnison, 1, UNISON, AUG, "Augmented Unison");
BOOST_CHECK(DimUnison.isAugmented());

TEST_IVL(Dim2Unison, 2, UNISON, AUG, "Augmented Unison");
BOOST_CHECK(Dim2Unison.isAugmented());

ut_chord

#include "../common/namespace_notes.h"
#include "../common/note.h"
#include "../common/interval.h"
#include "../common/chord.h"

#define BOOST_TEST_MODULE ChordTest
#include <boost/test/auto_unit_test.hpp>
#include <memory>


BOOST_AUTO_TEST_CASE(ChordConstructor)

typedef std::shared_ptr<Note> nt;
nt C = nt(new Note('C'));
nt E = nt(new Note('E'));
nt G = nt(new Note('G'));
nt B = nt(new Note('B'));

Interval PUnison = Interval(*C, *C); // cannot determine this interval
Chord C7 = Chord(C , E, G, B);
Chord C72 = Chord(B, G, E, C);
Chord C73 = Chord(E, G, C, B);

【问题讨论】:

没有_std::array这样的东西。 你实际上并不需要你自己的默认构造函数。 我认为我们需要一个完整的可编译示例来产生您遇到的问题或发布所有代码(如果它不是太大)。 我尝试了following 测试代码,它工作正常。我们将需要查看更多代码。 我敢打赌,如果没有“静态”,它会导致缓冲区及其索引归零,代码将无法工作。在 ctor 中初始化索引就可以了,至少就示例代码而言。 【参考方案1】:

首先,您不应包含 .cpp 文件。要解决您遇到的链接器问题,请关注the inclusion model:将您的函数定义放在模板的头文件中。

其次,我已经尝试了以下示例程序,它现在可以工作了 - 问题可能是由于链接器错误造成的。

阅读this SO question 了解有关包含 cpp 文件(和模板)的更多信息。

main.cpp:

#include <array>
#include "circularbuffer.h"


typedef CircularBuffer<char, 7> CB_Natural_T;
typedef CircularBuffer<int, 12> CB_Chromatic_T;

static CB_Natural_T WHITENOTES = CB_Natural_T();        // buffer of letter notes
static CB_Chromatic_T POSITIONS = CB_Chromatic_T(); 

int main()
   
    WHITENOTES.insert('C');
    WHITENOTES.insert('D');
    WHITENOTES.insert('E');
    WHITENOTES.insert('F');
    WHITENOTES.insert('G');
    WHITENOTES.insert('A');
    WHITENOTES.insert('B');

    // Initialize all positions
    for (int i = 0; i < 12; ++i)
        POSITIONS.insert(i);

    return 0;

循环缓冲区.h:

#ifndef _CIRCULAR_BUFFER_H
#define _CIRCULAR_BUFFER_H

#include <array>

template < class T, int SIZE> 
class CircularBuffer

private:
    int _index;
    int _size;
    std::array<T, SIZE> _buffer;
public:
    CircularBuffer() : _index(0), _size(SIZE), _buffer() 

    int length ()
    
        return _size;
    

    T& at (int index)
    
        realign(index);
        return _buffer.at(index);
    

    void insert (T data)
    
        realign(_index);
        _buffer.at(_index) = data;
        _index += 1;
    
    int index ()
    
        return _index;
    
private:
    int realign (int& index)
    
        if (index >= _size)
        
            index -= _size;
            realign(index);
         else if (index < 0)
        
            index += _size;
            realign(index);
        
        return index;
    
;

#endif

另外,请使用inclusion guards 确保您的文件不会被包含两次。

【讨论】:

我没有包括包含保护,因为我在顶部有很大的文档标题,但它们在那里。另外,我现在更改了模板类,在 cpp 中添加了模板类 CircularBuffer 和 for char,以便它找到这两种类型的实现。但是,在我的情况下,WHITENOTES 似乎根本没有修改任何成员。所以我没有看到索引增加,也没有看到缓冲区添加内容。但我确实在函数本身内部再次看到了它。你认为这可能是来自另一个项目的电话的问题吗?不回ut? 将模板定义放在标题中是否解决了您的问题? @David 你不应该有一个单独的 .cpp 定义 - 将定义放在头文件中,你将不再需要包含 .cpp 文件(你真的 不应该这样做)。完成此操作后,看看您是否仍有问题。 不幸的是它没有。我认为这个问题可能是项目 ma 的回报在回到 ut 项目时被破坏了。这可能吗? 这绝对是可能的 - 你能用我能理解的最低限度的来源更新你最初的问题吗?【参考方案2】:
static CB_Natural_T WHITENOTES = CB_Natural_T();
static CB_Chromatic_T POSITIONS = CB_Chromatic_T();

是这两个不符合您的预期,对吧?由于这些是全局变量,因此您应该放置

extern CB_Natural_T WHITENOTES;
extern CB_Chromatic_T POSITIONS;

到头文件中声明它们并

CB_Natural_T WHITENOTES;
CB_Chromatic_T POSITIONS;

到一个 cpp 文件中来实际定义它们。 static 导致这些对象具有内部链接,因此每个包含标头的文件(准确地说:编译单元)都会创建两个这样的对象,而不是在不同的文件之间共享它们。

我也认为这两个对象是常量,对吧?在这种情况下,您可以这样声明它们。然后,您将需要一个生成这些对象的助手或一个允许初始化的构造函数:

CB_Natural_T whitenotes()

    CB_Natural_T init;
    ...
    return init;

CB_Natural_T const WHITENOTES = whitenotes();

注意事项:

如前所述,“= T()”是多余的。 模板 SIZE 参数存储在 int 中,这是不必要的,因为该值始终存在。 您正在使用realign() 函数,该函数既修改参数又返回结果。我只会使用其中之一。此外,由于它是一个只修改参数而不触及任何成员的函数(见上文!),您可以将其设为静态函数。至少它应该是一个 const 成员函数。

【讨论】:

感谢您的解释,这可能就是我看到两个实例的原因。我以前从未见过这种情况,所以现在我将在头文件中定义全局变量时采取更多预防措施。此外,SIZE 模板用于定义数组成员,因为它需要一个 int 作为参数。我想成员 _size 有点多余,但我不太确定返回模板“成员”是否是编写类的正确方法?您能否再解释一下使用“仅其中一个”进行重新调整是什么意思? 假设这段代码:int i = 5; int k = realign(i); 两个整数 i 和 k 具有完全相同的值,因为 realign() 调整了作为参数传递的整数,另外它返回相同的值。我要么按值传递一个整数,要么按值返回对齐的索引。顺便说一句:查看模运算符以实现该功能!

以上是关于c++ 对象正在创建同一个数组的两个实例,但在不同的范围内?的主要内容,如果未能解决你的问题,请参考以下文章

创建不同对象的实例列表

循环遍历两个对象数组以将匹配值推送到新数组在 React 中不起作用,但在 JS Fiddle 中起作用

为啥我的对象会在 C++ 中被覆盖?

小白学习C++ 教程十一C++类中访问修饰符

java中数组和集合的区别是什么?

java中数组和集合的区别是什么?