C++ C++;:有没有一种有效的方法在语法上使用指针之类的索引?
这是我的旅程,我试图为一个我怀疑经常出现的问题获得一些零开销语法便利。我对C++很陌生,所以我希望有人能给我一个更好的解决方案。给出的所有代码都应该在C++11下编译和运行。还要注意的是,尽管我的解决方案涉及到使用指针的语法,但我对“与使用指针一样简单”很满意 我有一些代码使用对象网络,对象之间有指针,如下所示。(为了简单起见,我这里的示例只是一个链表,但实际结构更为复杂-a。)C++ C++;:有没有一种有效的方法在语法上使用指针之类的索引?,c++,pointers,C++,Pointers,这是我的旅程,我试图为一个我怀疑经常出现的问题获得一些零开销语法便利。我对C++很陌生,所以我希望有人能给我一个更好的解决方案。给出的所有代码都应该在C++11下编译和运行。还要注意的是,尽管我的解决方案涉及到使用指针的语法,但我对“与使用指针一样简单”很满意 我有一些代码使用对象网络,对象之间有指针,如下所示。(为了简单起见,我这里的示例只是一个链表,但实际结构更为复杂-a。) #包括 结构节点{ int-val; 节点*下一步; }; int main() { Node*a=新节点(); N
#包括
结构节点{
int-val;
节点*下一步;
};
int main()
{
Node*a=新节点();
Node*b=新节点();
a->val=1;
b->val=2;
a->next=b;
std::cout next->val(){return&(*collection)[index];}
};
结构节点{
int-val;
IndexPtr next;
};
int main()
{
std::向量节点列表{2};
自动a=IndexPtr(0,节点列表);
自动b=IndexPtr(1,节点列表);
a->val=1;
b->val=2;
a->next=b;
std::cout next->val我真的不喜欢静态方法。你似乎意识到,当你必须使用两个或更多集合时,这是一个巨大的问题,你永远不知道你不会。这只是一个糟糕的设计
经过深思熟虑,我想出了一个疯狂的解决方案:
class NodeList {
friend class NodeReference;
friend class NextReference;
private:
struct Node {
int val;
std::size_t next;
};
std::vector<Node> nodes;
public:
NodeList(std::size_t size) :
nodes(size) {}
inline NodeReference operator[](std::size_t index);
};
class NodeReference {
friend class NodeList;
friend class NextReference;
public:
int &val() { return list.nodes[index].val; }
inline NextReference next();
private:
NodeList &list;
std::size_t index;
NodeReference(NodeList &list, std::size_t index) :
list(list), index(index) {}
};
class NextReference {
friend class NodeReference;
public:
int &val()
{
return node.val();
}
NextReference operator=(NodeReference &node)
{
node.list.nodes[node.index].next = node.index;
return *this;
}
private:
NodeReference &node;
NextReference(NodeReference &node) :
node(node) {}
};
inline NodeReference NodeList::operator[](std::size_t index)
{
return NodeReference(*this, index);
}
inline NextReference NodeReference::next()
{
return NextReference(*this);
}
int main() {
NodeList nodeList(2);
auto a = nodeList[0];
auto b = nodeList[1];
a.val() = 1;
b.val() = 2;
a.next() = b;
std::cout << "Sum: " << a.val() + a.next().val() << std::endl;
}
因此,看起来编译器实际上可以通过所有中间引用进行斗争,并直接将必要的值分配到它们所属的位置。但代码内部看起来仍然很难看,我无法摆脱这样的感觉,即使用较少的辅助类可以使它变得更容易。定义基于int的节点怎么样Ptr
class
#include <vector>
#include <iostream>
struct Node;
std::vector<Node> nodes;
struct NodePtr {
int index = -1;
operator bool() const { return index != -1; }
Node& operator*() { return nodes[index]; }
Node* operator->() { return &nodes[index]; }
};
struct Node {
int val;
NodePtr next;
};
int main()
{
nodes.push_back({1});
nodes.push_back({2});
NodePtr a{0}, b{1};
a->next = b;
std::cout << "Sum: " << a->val + a->next->val << std::endl;
}
#包括
#包括
结构节点;
std::向量节点;
结构节点接受器{
int指数=-1;
运算符bool()常量{返回索引!=-1;}
节点和运算符*(){返回节点[索引];}
Node*运算符->(){return&nodes[index];}
};
结构节点{
int-val;
NodePtr-next;
};
int main()
{
nodes.push_back({1});
nodes.push_back({2});
NodePtr a{0},b{1};
a->next=b;
std::cout next->val在考虑了Sergey Tachenov的答案一段时间后,我认为可以做出合理的折衷。有一个包装器类同时知道索引和集合。它仍然有类似next()的方法
返回另一个包装器。但是,我们也允许使用运算符*
和运算符->
获取原始的未包装值
因此,我们不必使用复杂的方案来支持
a.next().next() = b;
相反,我们通常只在链的末尾使用->
,因此我们可以完全正常地修改或使用该值
a.next()->next = b;
下面是使用此方法编写的示例代码
#include <iostream>
#include <vector>
struct Node {
int val;
std::size_t next;
};
class IndexPointer {
std::size_t index;
std::vector<Node>& collection;
public:
IndexPointer(std::size_t index, std::vector<Node>& collection)
: index(index), collection(collection)
{ /* done */ }
inline operator std::size_t() {return index;}
inline Node& operator*() {return collection[index];}
inline Node* operator->() {return &collection[index];}
inline IndexPointer next() {return IndexPointer((*this)->next, collection);}
};
class NodeList {
std::vector<Node> nodes;
public:
NodeList(std::size_t n) : nodes(n) {}
inline IndexPointer operator[](std::size_t i) {return IndexPointer(i, nodes);}
};
int main()
{
NodeList nodes {2};
auto a = nodes[0];
auto b = nodes[1];
a->val = 1;
a->next = b;
a.next()->val = 2;
std::cout << "Sum: " << a->val + a.next()->val << std::endl;
}
#包括
#包括
结构节点{
int-val;
std::下一步的大小;
};
类索引导出器{
标准:尺寸指数;
std::向量和集合;
公众:
索引指针(标准::大小索引、标准::向量和集合)
:索引(索引)、集合(集合)
{/*完成*/}
内联运算符std::size_t(){return index;}
内联节点和运算符*(){return collection[index];}
内联节点*运算符->(){return&collection[index];}
inline IndexPointer next(){return IndexPointer((*this)->next,collection);}
};
类节点列表{
std::向量节点;
公众:
节点列表(std::size\u t n):节点(n){
内联IndexPointer运算符[](std::size_t i){return IndexPointer(i,节点);}
};
int main()
{
节点列表节点{2};
自动a=节点[0];
自动b=节点[1];
a->val=1;
a->next=b;
a、 next()->val=2;
std::cout val那么,这一切是否归结为拥有一个也可以像数组一样索引的链表?您仍然可以使用指向向量中元素的指针,并将其递增以获得下一项。如果您还希望允许添加新节点而不使现有项无效,则使用列表
而不是向量
,并使用list::iterator
代替指针(这是一个包装指针并提供“下一步”操作的类)@PaulMcKenzie@M.M这里的网络结构只是一个例子。我的例子是一个链表是偶然的。事实上,我正在实现一个多面体的表示。它经历了大量的变化,所以普通指针将无效。出于指针追逐性能的原因,使用list
是不合适的。这是甚至比静态字段方法更糟糕,因为它本质上用一个全局变量替换静态字段。@SergeyTachenov:静态只是一个具有时髦名称的全局变量,没有区别。最大的区别是名称空间污染。@SergeyTachenov:只需将全局变量命名为MyClass\u static\u var
,您就会看到区别在这两者之间,MyClass::static\u var
并没有那么大(只是在编写方法时节省了一些输入)C++命名空间分区实际上并没有添加任何重要的东西。例如,在同一个项目中,除非您将它们放置在不同的命名空间中,否则不能有两个类:“代码”:Poto2D2</Cuth>,但是命名空间必须在源代码中明确指定。这使得例如使用两个版本的项目几乎不可能。我想你可以这样命名它,但是即使在使用它的类中,你也必须用这么长的名称来称呼它。如果它是唯一一个使用它的类,那看起来会很难看。另一方面,静态变量可以有一个简短而有意义的名称。我真的很喜欢你的想法,谢谢。A经过一段时间的思考,我认为我已经通过使用.next()
和->next
找到了一个很好的折衷方案。如果你想看一看,我已经添加了它作为答案。太棒了。这正是我想要做的,只是我没有做对,你做了。
; 62 : auto a = nodeList[0];
; 63 : auto b = nodeList[1];
; 64 : a.val() = 1;
; 65 : b.val() = 2;
; 66 : a.next() = b;
; 67 :
; 68 : std::cout << "Sum: " << a.val() + a.next().val() << std::endl;
mov eax, DWORD PTR _nodeList$[esp+36]
push OFFSET ??$endl@DU?$char_traits@D@std@@@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@@Z ; std::endl<char,std::char_traits<char> >
mov DWORD PTR [eax], 1
mov eax, DWORD PTR _nodeList$[esp+40]
mov DWORD PTR [eax+8], 2
mov eax, DWORD PTR _nodeList$[esp+40]
mov DWORD PTR [eax+12], 1
#include <vector>
#include <iostream>
struct Node;
std::vector<Node> nodes;
struct NodePtr {
int index = -1;
operator bool() const { return index != -1; }
Node& operator*() { return nodes[index]; }
Node* operator->() { return &nodes[index]; }
};
struct Node {
int val;
NodePtr next;
};
int main()
{
nodes.push_back({1});
nodes.push_back({2});
NodePtr a{0}, b{1};
a->next = b;
std::cout << "Sum: " << a->val + a->next->val << std::endl;
}
a.next().next() = b;
a.next()->next = b;
#include <iostream>
#include <vector>
struct Node {
int val;
std::size_t next;
};
class IndexPointer {
std::size_t index;
std::vector<Node>& collection;
public:
IndexPointer(std::size_t index, std::vector<Node>& collection)
: index(index), collection(collection)
{ /* done */ }
inline operator std::size_t() {return index;}
inline Node& operator*() {return collection[index];}
inline Node* operator->() {return &collection[index];}
inline IndexPointer next() {return IndexPointer((*this)->next, collection);}
};
class NodeList {
std::vector<Node> nodes;
public:
NodeList(std::size_t n) : nodes(n) {}
inline IndexPointer operator[](std::size_t i) {return IndexPointer(i, nodes);}
};
int main()
{
NodeList nodes {2};
auto a = nodes[0];
auto b = nodes[1];
a->val = 1;
a->next = b;
a.next()->val = 2;
std::cout << "Sum: " << a->val + a.next()->val << std::endl;
}