什么时候 public/private 的排序不是样式选择?

When is the ordering of public/private not a style choice?

所以通常我遵循将我的 public 成员函数放在我的私有属性之前的惯例,比如

class SomeClass {
public:
    void amazingMethod();
private:
    int fantasticAttribute;
};

因为用户(包括我)首先希望在 class 中看到的是我可以使用的东西。然而,我最近的一个项目引入了一些真正的怪异。

template<typename T>
class LinkedList {
public:
    class LinkedListIterator {
    public:
        LinkedListIterator(Node* node) : node(node) {}
        LinkedListIterator& operator++() {
            if (node != nullptr) {
                node = node->next;
            }
        }
    private:
        Node* node;
    };
    LinkedListIterator begin() {
        return LinkedListIterator(head);
    }
private:
    struct Node {
        Node* next;
    };
    Node* head;
};

此代码 在 Visual Studio 2015 年无法编译 ,报告当 LinkedListIterator 尝试使用它时未定义 Node。但奇怪的是,如果我翻转 LinkedList 所以它被声明为

template <typename T>
class LinkedList {
private:
    //everything that was in private before
public:
    //everything that was in public before
};

一切都正确编译!这只是一个 Visual Studio 怪癖吗?我猜这与它是一个模板这一事实有关 class 但我不知道。

我怎么知道什么时候我必须泪流满面地放弃首先宣布 public 成员(正如大多数惯例所建议的那样)?什么时候它不仅仅是一种风格选择?

它与 public 和 private 本身无关。它与Node在使用时是否已经定义有关。

与访问类型无关。它与 LinkListIterator 试图使用编译器尚未发现的类型有关。

但是,由于迭代器 class 仅使用指向 Node 类型的指针,因此前向声明就足够了。下面的代码添加了前向声明并编译得很好:

template<typename T>
class LinkedList {
private:
    struct Node; // Private forward declaration of the private class

public:
    class LinkedListIterator {
    public:
        LinkedListIterator(Node* node) : node(node) {}
        LinkedListIterator& operator++() {
            if (node != nullptr) {
                node = node->next;
            }
        }
    private:
        Node* node;
    };
    LinkedListIterator begin() {
        return LinkedListIterator(head);
    }
private:
    struct Node {
        Node* next;
    };
    Node* head;
};

int main()
{
    return 0;
}

Live Example

如果您想先声明 public 成员,请重写您的定义:

template<typename T>
class LinkedList {
public:
    class LinkedListIterator;
    LinkedListIterator begin();
private:
    struct Node;
    Node* head;
};

template<typename T>
struct LinkedList<T>::Node {
    Node* next;
};

template<typename T>
class LinkedList<T>::LinkedListIterator {
public:
    LinkedListIterator(Node* node);
    LinkedListIterator& operator++();
private:
    Node* node;
};

template<typename T>
LinkedList<T>::LinkedListIterator::LinkedListIterator(Node* node) : node(node) {}

template<typename T>
typename LinkedList<T>::LinkedListIterator& LinkedList<T>::LinkedListIterator::operator++() {
    if (node != nullptr) {
        node = node->next;
    }
    return *this;
}

template<typename T>
typename LinkedList<T>::LinkedListIterator LinkedList<T>::begin() {
    return LinkedListIterator(head);
}