0

我对 C++ 很陌生,作为练习(也许最终是 .Net 实用程序),我正在做一个指针包装器(实际上是在 C++/CLI 中,但这也适用于 C++)。如果标记为1.2.的行被注释掉,这个指针包装器(称为Apont)当前的行为就像指针一样,如下面的测试所示:

int main(array<System::String ^> ^args)
{
    double ia = 10; double ip = 10;
    double *p = &ip;                // pointer analogy
    Apont<double> ^a =
        gcnew Apont<double>(ia);    // equivalent to what's below, without errors
    a = ~ia;/* 1.       IntelliSense: expression must have integral or unscoped enum type
                        error C2440: '=' : cannot convert from 'double' to 'Utilidades::ComNativos::Apont<T> ^'
                        error C2171: '~' : illegal on operands of type 'double'*/
    Console::WriteLine("ip = {0}; *p = {1}; ia = {2}; !a = {3}", ip, *p, ia, !a);
    ia = 20; ip = 20;
    Console::WriteLine("ip = {0}; *p = {1}; ia = {2}; !a = {3}", ip, *p, ia, !a);
    *p = 30;        // pointer analogy
    a->Valor = 30;  // does exacly what's below, without errors
    !a = 30;/* 2.   IntelliSense: expression must be a modifiable lvalue
                    error C2106: '=' : left operand must be l-value */
    Console::WriteLine("ip = {0}; *p = {1}; ia = {2}; !a = {3}", ip, *p, ia, !a);
    //a->Dispose();
    Console::ReadKey();
    p = nullptr;
    return 0;
}

这里有两件事我不喜欢,在代码注释中用1.2.标记,在有错误的行之前。operator~1.)在 外部定义Apont,如下:

template<typename T> static Apont<T>^% operator ~(T& valor)
{
    return gcnew Apont<T>(valor);
}

我认为这个必须在外部定义Apont,但我不确定。我不能很好地理解它产生的错误(当然,这些是在使用中,而不是在定义中)。

要设置Apont引用的实例的值,我必须使用一个属性(标记为2.的行不起作用,仅在设置用法中有错误),Apont::Valor它等效于 use *p。我想做的是当我*p用来获取或设置它指向的值时,!aApont. 这是Apont::operator!()的当前定义:

T operator !()
{
    return Valor;
}

正如您在2中看到的那样(在代码中的注释中,在相应的错误之前),它不适用于设置值。也许我应该返回一个参考?制作另一个具有相同名称的运算符,可能在类之外?我尝试了几个选项,但是,我遇到了类似的错误,并且更加困惑。

问题是:如何创建一个行为类似于&(在本例中为~)的运算符和一个行为类似于*(在本例中!为 , 用于取消引用,但行为类似于Apont::Valor,您可以在下面看到其旧定义)的运算符?

property T Valor
{
    T get()
    {
        if (pointer != nullptr)             
            return *pointer;
        else if (eliminado && ErroSeEliminado) // means "disposed && ErrorIfDisposed"
            throw gcnew ObjectDisposedException("O objeto já foi pelo menos parcialmente eliminadao.");
        else if (ErroSeNulo) // means "ErrorIfNull"
            throw gcnew NullReferenceException();
        else
            return 0;
// don't worry, this is not default behavior, it is returned only if you want to ignore all errors and if the pointer is null
    }
    void set(T valor)
    {
        *pointer = valor;
    }
}
4

2 回答 2

1

如果我正确理解了您的代码,您希望操作员~返回指针包装器的副本,并且操作员!充当取消引用?

在这种情况下,您可以在调用复制构造函数operator ~的类中定义一元。Apont如果operator !您想分配一个值,则必须确实返回一个引用。

我认为下面的 c++ 代码定义了你想要做什么(我重命名ApontA):

#include <iostream>

template<typename T>
struct A {
    T* payload;

    A(T *ptr)
        :payload(ptr) {}
    A(const A&other)
        :payload(other.payload) {}

    T& operator !(){
        return *payload;
    }

    T* operator ~(){
        return payload;
    }
};

int main(){
#define PRINT(X) std::cerr << #X << " = " << X << std::endl
    int i = 0;
    PRINT(i);

    A<int> a(&i);
    !a = 1;
    PRINT(i);

    A<int> b = ~a;
    !b = 2;
    PRINT(i);
}

上面代码的输出是:

i = 0
i = 1
i = 2

根据您的评论,您说您希望运算符!的行为与包装指针完全相同。您可以这样做,但随后语法会发生变化,您需要取消引用它以分配新值(因为它是一个指针......)。即类似的东西:

#include <iostream>

template<typename T>
struct A {
    T* payload;

    A(T *ptr): payload(ptr) {}

    // this now behaves like accessing the wrapped pointer directly
    T*& operator !(){
        return payload;
    }
};

int main(){
#define PRINT(X) std::cerr << #X << " = " << X << std::endl
    int i = 0;
    int j = 999;
    PRINT(i);

    A<int> a(&i);
    *(!a) = 1;  // note the change of syntax here
    PRINT(*!a); // and here

    !a = &j;    // but now you can change the wrapped pointer through the operator
    PRINT(*!a);
}
于 2013-04-20T14:01:55.423 回答
1

为了清楚起见,让我回顾一下新的答案。

!正如我在之前的回答中所说,解决运算符很容易,只需添加参考即可。

所以对于 operator ~,目标是让它表现得像&operator 并调用指针包装类的构造函数。

我不认为这是可能的。用户定义的对象当然是可能的,但我认为不可能为内置类型重载一元运算符。因此,根据您的喜好,有三种解决方案:

第一个完全符合您的要求,但会因原始类型而中断:

#include <iostream>

template<typename T>
struct A {
    T* payload;

    A()
        : payload(NULL){}
    A(T *ptr)
        : payload(ptr) {}

    T& operator !(){
        return *payload;
    }
};

// this will not work for primary types
template<typename T>
A<T> operator ~(T &b){
    return A<T>(&b);
}

struct B{
    int test;
};

int main(){
    B b; b.test = 4;

    A<B> a;
    a = ~b; // I think this is what you want
    std::cerr << (!a).test << std::endl;

    // this does not work
    //int i = 4;
    //A<int> a;
    //a = ~i;
}

第二种解决方案:使用复合赋值运算符。优点是副作用很小,缺点是这不是很直观,可能会破坏您心中的好设计。

#include <iostream>

template<typename T>
struct A {
    T* payload;

    A() : payload(NULL){}

    T& operator !(){
        return *payload;
    }
};

template<typename T>
A<T>& operator &=(A<T> &a, T& b){ // should be friend of the above
    a.payload = &b;
    return a;
}

int main(){
    int i = 3;
    A<int> a;
    a &= i;
    std::cerr << !a << std::endl;
}

第三种解决方案:重载基本赋值运算符。这写起来更直观,但有很多副作用:

#include <iostream>

template<typename T>
struct A {
    T* payload;

    A() : payload(NULL){}

    T& operator !(){
        return *payload;
    }

    A<T>& operator = (T & b) {
        payload = &b;
        return *this;
    }
};

int main(){
    int i = 3;
    A<int> a;
    a = i;
    std::cerr << !a << std::endl;
}

有人可能有一个解决方案来劫持原始类型的运算符,但我想不出任何简单的解决方案。

于 2013-04-20T16:20:08.613 回答