遍历指针数组 C++
Iterating over pointer array C++
我是 C++ 的新手,但我有 C 的经验。现在我试图遍历一个对象数组,但出现分段错误,因为它超出了数组的范围。
我有一个 class 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;
};
这个class的构造函数接受一个数字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
超出了这个 main 的数组范围:
#include "ZombieHorde.hpp"
int main()
{
ZombieHorde horde(4);
horde.announce();
}
现在的主要问题是如何遍历 zombies 数组以免出现分段错误。
注意:请注意,这是一个 42 的学校练习,不允许我使用 C++ 的 STL。我应该在构造函数中立即分配僵尸。
当我阅读您问题的给定标题时,我认为您确实有一个“指针数组”并对其进行迭代。但是你编码了一个 objects 的数组,这是另一回事。好的,让我们开始创建一个真正的指针数组...
您的问题是while (&this->zombies[i])
。这将测试结果是真还是假,如果你有指针,它们将被隐式转换为 bool,因为我们期望数组末尾有一个 nullptr
.
也许您将数组的分配和初始化更改为:
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 类”!
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();
}
question is how to iterate over the zombies array in order to not get a segmentation fault.
解决这个问题的一种方法是使用一个名为 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++;
}
}
我觉得你面临的实际问题是 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++ 时,我发现另一件事很重要:
类 通常不是简单的数据结构,而是它们的数据通常组合成满足某些规则的某个实体。这些规则通常通过禁止成员数据的某些组合来体现,通常称为“class 不变量”。
例如这里有一个没有类型或名称的僵尸可能是无稽之谈。我喜欢以这样一种方式设计我的 classes,即在构建后它们满足它们的规则。
如果你不这样做,你必须在每次使用僵尸时检查它是否已经调用了它的初始化函数 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.
}
}
我是 C++ 的新手,但我有 C 的经验。现在我试图遍历一个对象数组,但出现分段错误,因为它超出了数组的范围。
我有一个 class 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;
};
这个class的构造函数接受一个数字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
超出了这个 main 的数组范围:
#include "ZombieHorde.hpp"
int main()
{
ZombieHorde horde(4);
horde.announce();
}
现在的主要问题是如何遍历 zombies 数组以免出现分段错误。
注意:请注意,这是一个 42 的学校练习,不允许我使用 C++ 的 STL。我应该在构造函数中立即分配僵尸。
当我阅读您问题的给定标题时,我认为您确实有一个“指针数组”并对其进行迭代。但是你编码了一个 objects 的数组,这是另一回事。好的,让我们开始创建一个真正的指针数组...
您的问题是while (&this->zombies[i])
。这将测试结果是真还是假,如果你有指针,它们将被隐式转换为 bool,因为我们期望数组末尾有一个 nullptr
.
也许您将数组的分配和初始化更改为:
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 类”!
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();
}
question is how to iterate over the zombies array in order to not get a segmentation fault.
解决这个问题的一种方法是使用一个名为 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++;
}
}
我觉得你面临的实际问题是 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++ 时,我发现另一件事很重要: 类 通常不是简单的数据结构,而是它们的数据通常组合成满足某些规则的某个实体。这些规则通常通过禁止成员数据的某些组合来体现,通常称为“class 不变量”。
例如这里有一个没有类型或名称的僵尸可能是无稽之谈。我喜欢以这样一种方式设计我的 classes,即在构建后它们满足它们的规则。 如果你不这样做,你必须在每次使用僵尸时检查它是否已经调用了它的初始化函数 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.
}
}