【问题标题】:Cannot access private member of the same class无法访问同一类的私有成员
【发布时间】:2013-04-02 02:10:07
【问题描述】:

我正在实现一个智能指针类作为练习。考虑以下几点:

class Base1 {
    protected:
        Base1() : derived_destructor_called(false) {
            printf("Base1::Base1()\n");
        }
    private:
        Base1(const Base1 &); // Disallow.
        Base1 &operator=(const Base1 &); // Disallow.
    protected:
        ~Base1() {
            printf("Base1::~Base1()\n");
            assert(derived_destructor_called);
        }
    protected:
        bool derived_destructor_called;
};

class Derived : public Base1 {
        friend void basic_tests_1();
    private:
        Derived() {printf("Derived::Derived()\n");}
        Derived(const Derived &); // Disallow.
        Derived &operator=(const Derived &); // Disallow.
    public:
        ~Derived() {
            printf("Derived::~Derived()\n");
            derived_destructor_called = true;
        }
        int value;
};

以及下面的测试代码:

            Sptr<Derived> sp(new Derived);
            // // Test template copy constructor.
            Sptr<Base1> sp3(sp);

产生以下错误:

Sptr.cpp: In instantiation of ‘my::Sptr<T>::Sptr(const my::Sptr<U>&) [with U = Derived; T = Base1]’:
Sptr.cpp:272:35:   required from here
Sptr.cpp:41:6: error: ‘Derived* my::Sptr<Derived>::obj’ is private
Sptr.cpp:117:86: error: within this context
Sptr.cpp:42:13: error: ‘my::RC* my::Sptr<Derived>::ref’ is private
Sptr.cpp:117:86: error: within this context
Sptr.cpp:43:31: error: ‘std::function<void()> my::Sptr<Derived>::destroyData’ is private
Sptr.cpp:117:86: error: within this context

这怎么可能?我指的是同一个类中的同一个类变量。!

下面是模板类原型及其构造函数声明:

template <class T>
class Sptr {
private:
    //some kind of pointer
        //one to current obj
    T* obj;
    RC* ref;
    std::function<void()> destroyData;
public:
    Sptr();
    ~Sptr();

    template <typename U> 
    Sptr(U *);

    Sptr(const Sptr &);

    template <typename U> 
    Sptr(const Sptr<U> &);

    template <typename U> 
    Sptr<T> &operator=(const Sptr<U> &);

    Sptr<T> &operator=(const Sptr<T> &);

    void reset();

    T* operator->() const
    {return obj;};

    T& operator*() const
    {return *obj;};

    T* get() const
    {return &obj;};

    //operator unspecified_bool_type() const;

    //overload *,->,=,copy-constructor

    // const-ness should be preserved.
    // Test for null using safe-bool idiom
    // Static casting, returns a smart pointer
};

编辑

我在模板类中声明了一个模板友元如下:

template<typename U> friend class Sptr;

解决了上述错误。但它产生了新的错误!

测试代码:

        Sptr<Base1> sp2;
        {
            Sptr<Derived> sp(new Derived);
            // // Test template copy constructor.
            Sptr<Base1> sp3(sp);
            sp2 = sp;
            sp2 = sp2;
        }

错误:

Sptr.cpp: In instantiation of ‘my::Sptr<T>& my::Sptr<T>::operator=(const my::Sptr<U>&) [with U = Derived; T = Base1]’:
Sptr.cpp:274:23:   required from here
Sptr.cpp:128:9: error: comparison between distinct pointer types ‘my::Sptr<Base1>*’ and ‘const my::Sptr<Derived>*’ lacks a cast
Sptr.cpp:212:9: error: ‘Base1::~Base1()’ is protected
Sptr.cpp:132:21: error: within this context
Sptr.cpp: In instantiation of ‘my::Sptr<T>& my::Sptr<T>::operator=(const my::Sptr<T>&) [with T = Base1]’:
Sptr.cpp:275:23:   required from here
Sptr.cpp:212:9: error: ‘Base1::~Base1()’ is protected
Sptr.cpp:154:21: error: within this context

当我已经将它设为模板朋友时,这怎么可能? = 运算符下面重载了

template <typename T> 
Sptr<T>& Sptr<T>::operator=(const Sptr<T> &t) {

    std::cout<<"= const <T>\n";
    if(this != &t) {
        if(ref->Release() == 0) {

            if(obj)
                delete obj;

            delete ref;       
        }

        obj = t.obj;
        ref = t.ref;
        destroyData = t.destroyData;
        ref->AddRef();
    }

    return *this;
}
template <typename T> 
template <typename U> 
Sptr<T>& Sptr<T>::operator=(const Sptr<U> &t) {

    std::cout<<"= const <U>\n";
    if(this != &t) {
        if(ref->Release() == 0) {

            if(obj)
                delete obj;

            delete ref;       
        }

        obj = t.obj;
        ref = t.ref;
        destroyData = t.destroyData;
        ref->AddRef();
    }

    return *this;
}

【问题讨论】:

  • 新错误不是由修复“创建”的,而是在消除了前一个错误的障碍后可能揭示
  • 查看我对新错误的更新答案

标签: c++ templates c++11 g++ smart-pointers


【解决方案1】:

班级不同;你的构造函数是

my::Sptr<Base1>::Sptr(const my::Sptr<Derived>&)

并且您正在尝试访问my::Sptr&lt;Derived&gt; 的成员。

你需要声明一个模板友元:

template<typename U> friend class Sptr;

【讨论】:

  • 你说的有效,但看看我的问题编辑。我该如何解决?它本质上又是同样的问题。
  • 一旦我声明了你说的模板朋友,它里面的所有函数不会成为朋友吗?
  • @footy 是的,就是这样。如果您不希望这样,请提供公共方法来获取指针和引用计数,或者提供方法以通过一些接受引用计数的构造函数将Sptr&lt;U&gt; 显式转换为Sptr&lt;T&gt;
【解决方案2】:

我指的是同一个类中的同一个类变量。

不,你不是。也许你指的是同一个类的相同成员模板,我猜像

    template <typename U> 
    Sptr(const Sptr<U> & other) : obj(other.obj), ref(other.ref) {}

但是您没有访问同一个的成员,因为Sptr&lt;Base&gt;Sptr&lt;Derived&gt; 是模板的不同实例化,因此是不能接触的不同类彼此的隐私。

更新您的编辑:您现在遇到的错误与访问权限无关,但又与 Sptr&lt;Base&gt;Sptr&lt;Derived&gt; 是不同的类有关,因此指向它们的指针不同无法比较的指针类型。
您显示的代码是 operator= 用于相同的模板实例化 (Sptr&lt;T&gt;),但错误发生在不同模板实例化 (Sptr&lt;U&gt;) 的运算符中。我猜你那里也有地址比较,这不是必需的,因为Sptr&lt;T&gt; 不能与Sptr&lt;U&gt; 具有相同的地址。

关于受保护的~Base 的第二个错误很明显:您将~Base 设置为受保护且非虚拟的,这意味着您不会通过删除Base* 指针来破坏Base 对象。这同样适用于指向Base 的智能指针,因为它们会使用您禁止的多态删除。解决方案:将~Base 设为公共和虚拟。

【讨论】:

  • @Ame 我知道我可能听起来很奇怪。但这就是我必须通过的测试代码。解决方法是什么?还是有一个?
  • 好吧,测试代码没问题,但是Sptr 实现的一部分和~Base 声明为非虚拟保护不是。更改后者并修复模板化的operator=,您的测试代码将像魅力一样工作。
  • @Ame 你能给我一个关于如何使operator= 工作的提示吗?我对模板风格的编程有点陌生
  • @footy 正如我所说,留下地址比较,即删除行 if (this != &amp;t) 因为这是给出错误的比较,它永远不会是错误的。
  • a=a 不会是模板化的operator=,因为两者都是同一类型。从Sptr&lt;Base&gt; 转换为Sptr&lt;Derived&gt; 是不可能的,但如果是这样,转换会给你一个临时的Sptr&lt;Derived&gt; 对象,其地址显然与a 的地址不同。您将有两个对象指向相同的 ref 和 object,这不会造成太大的伤害,尽管您会不必要地复制该指针并释放并添加相同的 ref。因此,虽然您可以检查 ref 指针是否已经相同,但程序正确性没有必要。
【解决方案3】:

他们不是同一个班级。看起来它在抱怨您试图从 Sptr&lt;Base1&gt; 访问 Sptr&lt;Derived&gt; 的成员 - 查看错误消息开头的类型变量实例化。

模板,当它们的类型变量被填充时,会生成全新的类,它们恰好看起来非常相似,因此MyClass&lt;string&gt;MyClass&lt;vector&lt;int&gt;&gt; 不同。

【讨论】:

    猜你喜欢
    • 2021-12-25
    • 2015-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多