我是 C 新手,但我有 C 經驗。現在我正在嘗試迭代一個物件陣列,但我遇到了分段錯誤,因為它超出了陣列的范圍。
我有一個 ZombieHorede 類,它保存一個指向僵尸陣列的指標。
#pragma once
#include "Zombie.hpp"
class ZombieHorde {
private:
static std::string namepool[7];
static std::string typepool[7];
Zombie *zombies;
public:
ZombieHorde(int N);
~ZombieHorde(void);
void announce(void) const;
};
此類的建構式接受一個數字 N 并初始化 N 個僵尸陣列。像這樣。
ZombieHorde::ZombieHorde(int N)
{
int i;
i = 0;
this->zombies = new Zombie[N];
while (i < N)
{
this->zombies[i].set_name(this->namepool[rand() % 7]);
this->zombies[i].set_type(this->typepool[rand() % 7]);
i ;
}
}
現在我想遍歷僵尸并在每個僵尸上呼叫一個函式......就像這樣......
void ZombieHorde::announce() const
{
int i;
i = 0;
while (&this->zombies[i])
{
this->zombies[i].announce();
i ;
}
}
并且announce 函式給出了分段錯誤,因為i這個主函式超出了陣列的范圍:
#include "ZombieHorde.hpp"
int main()
{
ZombieHorde horde(4);
horde.announce();
}
現在的主要問題是如何迭代僵尸陣列以避免出現分段錯誤。
注意:請注意,這是一個 42 學校練習,我不允許使用 C 的 STL。我應該在建構式中立即分配僵尸。
uj5u.com熱心網友回復:
當我閱讀您的問題的給定標題時,我認為您確實有一個“指標陣列”并對其進行迭代。但是你撰寫了一個物件陣列,這是另一回事。好的,讓我們開始創建一個真正的指標陣列......
你的問題是while (&this->zombies[i])。這將測驗結果是真還是假,如果你有指標,它們將被隱式轉換為 bool,因為我們期望nullptr在陣列末尾有 a。
也許您將陣列的分配和初始化更改為:
Zombie **zombies = new Zombie*[N 1];
for ( unsigned int idx=0; idx<N; idx )
{
zombies[idx] = new Zombie;
}
zombies[N] = nullptr; // important to have the "end" mark if we later test for nullptr!
// use it later maybe per index
for ( unsigned int idx=0; idx<N; idx )
{
zombies[idx]->announce();
}
// or as you tried with pointer to nullptr compare
// you can also use your While instead if you initialize
// your running variable before outside the loop
for ( Zombie** ptr= zombies; *ptr; ptr )
{
std::cout << ptr << std::endl;
(*ptr)->announce();
}
順便說一句:不需要this在這種情況下寫!
但是你應該開始寫 C 而不是“C with classes”!
class Zombie
{
private:
std::string name;
std::string type;
public:
// you should always use constructors to set content
// of the class at the beginning instead later overwrite
// values. Making things private and write accessor's to
// all and everything is not the use case of encapsulation
Zombie( const std::string& name_, const std::string& type_): name{name_},type{type_}{}
void announce() const { std::cout << "Zombie " << name << ":" << type << std::endl; }
};
class ZombieHorde {
private:
static std::string namepool[7];
static std::string typepool[7];
std::vector<Zombie> zombies;
public:
ZombieHorde(const unsigned int N);
~ZombieHorde(){}
void announce() const;
};
std::string ZombieHorde::namepool[7] = { "One","two","three","four","five","six","seven"};
std::string ZombieHorde::typepool[7] = { "red","blue","green","yellow","pink","mouve","black"};
ZombieHorde::ZombieHorde(const unsigned int N)
{
for ( unsigned int idx=0; idx < N; idx )
{
zombies.emplace_back( this->namepool[rand() % 7], this->typepool[rand() % 7]);
}
}
void ZombieHorde::announce() const
{
// iterate over containers is now simplified to:
for ( const auto& zombie: zombies ) { zombie.announce(); }
}
int main()
{
ZombieHorde horde(4);
horde.announce();
}
uj5u.com熱心網友回復:
問題是如何迭代僵尸陣列以避免出現分段錯誤。
解決此問題的一種方法是使用一個名為的資料成員,該資料成員在修改后的代碼段中存盤如下所示zombieSize的值:N
class ZombieHorde {
private:
static std::string namepool[7];
static std::string typepool[7];
Zombie *zombies;
std::size_t zombieSize; //data member that is used to store the value of N
public:
ZombieHorde(int N);
~ZombieHorde(void);
void announce(void) const;
};
//use member initializer list to initialize zombieSize
ZombieHorde::ZombieHorde(int N): zombieSize(N)
{
int i;
i = 0;
this->zombies = new Zombie[zombieSize];//use data member zombieSize instead of N
while (i < zombieSize)//use zombieSize instead of N
{
this->zombies[i].set_name(this->namepool[rand() % 7]);
this->zombies[i].set_type(this->typepool[rand() % 7]);
i ;
}
}
void ZombieHorde::announce() const
{
int i;
i = 0;
while (i < zombieSize)//use zombieSize instead of N
{
this->zombies[i].announce();
i ;
}
}
uj5u.com熱心網友回復:
我覺得您面臨的實際問題是 C 和 C 之間的區別。
這樣做的 C 方法是使用龐大的標準庫,在本例中是一個向量:
#pragma once
#include <vector>
#include "Zombie.hpp"
class ZombieHorde {
private:
static std::vector<std::string> namepool(7); // Maybe initialize it here if the names are known at compiletime!
// if not I think passing the name and and typelist as an argument to the constructor is cleaner than using a static value here.
static std::vector<std::string> typepool(7);
std::vector<Zombie> zombies;
public:
ZombieHorde(int N);
// ~ZombieHorde(); as vector will take care of destruction you don't need to define or declare a destructor.
// using void in parameter list is not very customary in c but allowed, iirc.
void announce() const; // same thing
};
ZombieHorde::ZombieHorde(int N): zombies(N) // <- initializer list. This constructs the data member zombies with N as an argument for the constructor.
// The vector class can take an int as argument and default constructs that many copies into the container.
{
for (size_t i = 0;i!= zombies.size(); i){
this->zombies[i].set_name(this->namepool[rand() % 7]);
this->zombies[i].set_type(this->typepool[rand() % 7]);
}
}
注意:如果您的 C 標準(和編譯器)足夠新(c 17 就足夠了,不確定早期的標準,您可以使用基于以下的范圍:
ZombieHorde::ZombieHorde(int N): zombies(N) // <- initializer list
{
for (auto & zombie : zombies){
zombie.set_name(this->namepool[rand() % 7]);
zombie.set_type(this->typepool[rand() % 7]);
}
}
void ZombieHorde::announce() const
{
for( auto const & zombie : zombies)
{
zombie.announce();
}
}
在切換到 C 時,我發現另一件事很重要:類通常不僅僅是資料結構,而且通常它們的資料組合到滿足某些規則的某個物體。這些規則通常通過禁止成員資料的某些組合來體現,通常稱為“類不變數”。
例如,這里有一個沒有型別或名稱的僵尸可能是無稽之談。我喜歡以這樣一種方式設計我的課程,以便在構建后它們符合他們的規則。如果你不這樣做,你必須在每次使用僵尸時檢查它是否已經呼叫了它的初始化函式 set_name 和 set_type。
相反,您可以使僵尸具有 const 成員名稱和型別,并且只提供一個提供這些的建構式:
class Zombie{
public:
Zombie(std::string const & name, std::string const & type);
private:
std::string const name;
std::string const type;
};
Zombie::Zombie(std::string const & _name, std::string const & _type): name(_name),type(_type) {}
然后部落必須呼叫適當的建構式:
ZombieHorde::ZombieHorde(int N)
{
for(int i = 0;i!=N; i){
zombies.emplace_back(namepool[rand() % 7],typepool[rand() % 7]);
// emplace_back is neat: It takes the same argument as the constructor of an array element.
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/419683.html
標籤:
下一篇:無法從函式回傳字串
