Наличие Общественных собственностей в классе C++

Как у меня есть свойства в классе C++, как Вы имеете в классе C#.

Я не хочу иметь методы получателя и методы установщика.

12
задан Thomas Bonini 1 April 2010 в 12:50
поделиться

7 ответов

Вы можете использовать решение, подобное тому, что предложил Jon, сохраняя при этом обычную семантику C++ с помощью перегрузки операторов. Я немного изменил код Джона следующим образом (пояснения следуют за кодом):

#include <iostream>

template<typename T>
class Accessor {
public:
    explicit Accessor(const T& data) : value(data) {}

    Accessor& operator=(const T& data) { value = data; return *this; }
    Accessor& operator=(const Accessor& other) { this->value = other.value; return *this; }
    operator T() const { return value; }
    operator T&() { return value; }

private:
    Accessor(const Accessor&);


    T value;

};

struct Point {
    Point(int a = 0, int b = 0) : x(a), y(b) {}
    Accessor<int> x;
    Accessor<int> y;
};

int main() {
    Point p;
    p.x = 10;
    p.y = 20;
    p.x++;
    std::cout << p.x << "," << p.y << std::endl;

    p.x = p.y = 15;
    std::cout << p.x << "," << p.y << std::endl;

    return 0;
}

Мы перегружаем operator=, чтобы сохранить обычный синтаксис присваивания вместо синтаксиса, подобного вызову функции. Мы используем оператор cast в качестве "геттера". Нам нужна вторая версия operator=, чтобы разрешить присваивание второго рода в main().

Теперь вы можете добавить к конструктору Accessor функции-указатели, или лучше - функторы - для вызова в качестве геттеров/сеттеров любым способом, который кажется вам правильным. Следующий пример предполагает, что функция-сеттер возвращает bool, чтобы передать согласие на установку нового значения, а геттер может просто изменить его на выходе:

#include <iostream>
#include <functional>
#include <cmath>

template<typename T>
class MySetter {
public:
    bool operator()(const T& data)
    {
        return (data <= 20 ? true : false);
    }
};

template<typename T>
class MyGetter {
public:
    T operator()(const T& data)
    {
        return round(data, 2);
    }

private:
    double cint(double x) {
        double dummy;
        if (modf(x,&dummy) >= 0.5) {
            return (x >= 0 ? ceil(x) : floor(x));
        } else {
            return (x < 0 ? ceil(x) : floor(x));
        }
    }

    double round(double r, int places) {
        double off = pow(10.0L, places);
        return cint(r*off)/off;
    }
};

template<typename T, typename G = MyGetter<T>, typename S = MySetter<T>>
class Accessor {
public:
    explicit Accessor(const T& data, const G& g = G(), const S& s = S()) : value(data), getter(g), setter(s) {}

    Accessor& operator=(const T& data) { if (setter(data)) value = data; return *this; }
    Accessor& operator=(const Accessor& other) { if (setter(other.value)) this->value = other.value; return *this; }
    operator T() const { value = getter(value); return value;}
    operator T&() { value = getter(value); return value; }

private:
    Accessor(const Accessor&);

    T value;

    G getter;
    S setter;

};

struct Point {
    Point(double a = 0, double b = 0) : x(a), y(b) {}
    Accessor<double> x;
    Accessor<double> y;
};

int main() {
    Point p;
    p.x = 10.712;
    p.y = 20.3456;
    p.x+=1;
    std::cout << p.x << "," << p.y << std::endl;

    p.x = p.y = 15.6426;
    std::cout << p.x << "," << p.y << std::endl;

    p.x = p.y = 25.85426;
    std::cout << p.x << "," << p.y << std::endl;

    p.x = p.y = 19.8425;
    p.y+=1;
    std::cout << p.x << "," << p.y << std::endl;

    return 0;
}

Однако, как показывает последняя строка, здесь есть ошибка. Оператор cast, возвращающий T&, позволяет пользователям обойти сеттер, поскольку он дает им доступ к частному значению. Один из способов решения этой проблемы - реализовать все операторы, которые вы хотите, чтобы предоставлял ваш Accessor. Например, в следующем коде я использовал оператор +=, и поскольку я удалил оператор cast, возвращающий ссылку, мне пришлось реализовать operator+=:

#include <iostream>
#include <functional>
#include <cmath>

template<typename T>
class MySetter {
public:
    bool operator()(const T& data) const {
        return (data <= 20 ? true : false);
    }
};

template<typename T>
class MyGetter {
public:
    T operator() (const T& data) const {
        return round(data, 2);
    }

private:
    double cint(double x) const {
        double dummy;
        if (modf(x,&dummy) >= 0.5) {
            return (x >= 0 ? ceil(x) : floor(x));
        } else {
            return (x < 0 ? ceil(x) : floor(x));
        }
    }

    double round(double r, int places) const {
        double off = pow(10.0L, places);
        return cint(r*off)/off;
    }
};

template<typename T, typename G = MyGetter<T>, typename S = MySetter<T>>
class Accessor {
private:
public:
    explicit Accessor(const T& data, const G& g = G(), const S& s = S()) : value(data), getter(g), setter(s) {}

    Accessor& operator=(const T& data) { if (setter(data)) value = data; return *this; }
    Accessor& operator=(const Accessor& other) { if (setter(other.value)) this->value = other.value; return *this; }
    operator T() const { return getter(value);}

    Accessor& operator+=(const T& data) { if (setter(value+data)) value += data; return *this; }

private:
    Accessor(const Accessor&);

    T value;

    G getter;
    S setter;

};

struct Point {
    Point(double a = 0, double b = 0) : x(a), y(b) {}
    Accessor<double> x;
    Accessor<double> y;
};

int main() {
    Point p;
    p.x = 10.712;
    p.y = 20.3456;
    p.x+=1;
    std::cout << p.x << "," << p.y << std::endl;

    p.x = p.y = 15.6426;
    std::cout << p.x << "," << p.y << std::endl;

    p.x = p.y = 25.85426;
    std::cout << p.x << "," << p.y << std::endl;

    p.x = p.y = 19.8425;
    p.y+=1;
    std::cout << p.x << "," << p.y << std::endl;

    return 0;
}

Вам придется реализовать все операторы, которые вы собираетесь использовать.

12
ответ дан 2 December 2019 в 05:27
поделиться

Для подобного поведения я использую шаблонный мета-аксессор. Вот очень упрощенный вариант для типов POD:

template<class T>
struct accessor {

    explicit accessor(const T& data) : value(data) {}
    T operator()() const { return value; }
    T& operator()() { return value; }
    void operator()(const T& data) { value = data; }

private:

    accessor(const accessor&);
    accessor& operator=(const accessor&);
    T value;

};

Типичное использование выглядит следующим образом:

struct point {
    point(int a = 0, int b = 0) : x(a), y(b) {}
    accessor<int> x;
    accessor<int> y;
};

point p;
p.x(10);
p.y(20);
p.x()++;
std::cout << p.x();

Компилятор обычно встраивает эти вызовы, если вы все настроили правильно и включили оптимизацию. Независимо от того, какие оптимизации происходят, это не более узкое место в производительности, чем использование реальных геттеров и сеттеров. Это тривиально расширить, чтобы автоматически поддерживать не-POD или перечислимые типы или разрешить регистрацию обратных вызовов всякий раз, когда данные читаются или записываются.

Изменить : если вы предпочитаете не использовать круглые скобки, вы всегда можете определить operator = () и неявный оператор приведения. Вот версия, которая делает именно это, а также добавляет базовую поддержку обратного вызова «что-то произошло»:

Дальнейшее редактирование : Хорошо, я совершенно пропустил, что кто-то уже сделал исправленную версию моего кода. Вздох.

7
ответ дан 2 December 2019 в 05:27
поделиться

Свойства не поддерживаются в C++, но вы можете реализовать их:
1) Используя шаблоны
2) С помощью расширения языка и написания препроцессора кода

Любой из этих подходов не будет простым, но это можно сделать.

1
ответ дан 2 December 2019 в 05:27
поделиться

Если вас не волнует, что ваш код на C++ не будет компилироваться ничем, кроме компилятора Microsoft Visual C++, то вы можете использовать некоторые нестандартные расширения компилятора.

Например, следующий код создаст C#-подобное свойство под названием MyProperty.

struct MyType
{
    // This function pair may be private (for clean encapsulation)
    int get_number() const { return m_number; }
    void set_number(int number) { m_number = number; }

    __declspec(property(get=get_number, put=set_number)) int MyProperty;
private:
    int m_number:
}

int main()
{
    MyType m;
    m.MyProperty = 100;
    return m.MyProperty;
}

Более подробную информацию об этом специфическом для Microsoft расширении языка можно найти здесь.

3
ответ дан 2 December 2019 в 05:27
поделиться

Вы можете предоставить методы получения и установки с похожими именами. к элементам данных:

class Example
{
  private:
     unsigned int x_;
     double d_;
     std::string s_s;
  public:
     unsigned int x(void) const
     { return x_;}

     void x(unsigned int new_value)
     { x_ = new_value;}

     double d(void) const
     { return d_;}
     void d(double new_value)
     { d_ = new_value;}

     const std::string& s(void) const
     { return s_;}
     void s(const std::string& new_value)
     { s_ = new_value;}
};

Хотя это и близко, так как требует использования '()' для каждого члена, это не соответствует точной функциональности свойств , предоставляемых Microsoft Languages.

Наиболее близким подходом к свойствам является объявление элементов данных общедоступными.

1
ответ дан 2 December 2019 в 05:27
поделиться

Вот реализация PoC, которую я сделал некоторое время назад, работает хорошо, за исключением того, что вам нужно что-то настроить в конструкторе, чтобы он работал хорошо и плавно.

http://www.codef00.com/code/Property.h

Вот пример использования:

#include <iostream>
#include "Property.h"


class TestClass {
public:
    // make sure to initialize the properties with pointers to the object
    // which owns the property
    TestClass() : m_Prop1(0), m_Prop3(0.5), prop1(this), prop2(this), prop3(this) {
    }

private:
    int getProp1() const {
        return m_Prop1;
    }

    void setProp1(int value) {
        m_Prop1 = value;
    }

    int getProp2() const {
        return 1234;
    }

    void setProp3(double value) {
        m_Prop3 = value;
    }

    int m_Prop1;
    double m_Prop3;

public:
    PropertyRW<int, TestClass, &TestClass::getProp1, &TestClass::setProp1> prop1;
    PropertyRO<int, TestClass, &TestClass::getProp2> prop2;
    PropertyWO<double, TestClass, &TestClass::setProp3> prop3;
};

и некоторое использование этого класса ...

int main() {
    unsigned int a;
    TestClass t;
    t.prop1 = 10;
    a = t.prop1;
    t.prop3 = 5;
    a = t.prop2;
    std::cout << a << std::endl;
    return 0;
}

У этого подхода есть два неудобства:

  1. Вы должны дать свойству указатель на его класс-владелец.
  2. Синтаксис объявления свойства является подробным , но держу пари, что могу немного поправить это с помощью некоторых макросов
3
ответ дан 2 December 2019 в 05:27
поделиться

Вам не хватает. C ++ не поддерживает такие свойства, как C #. Если вы хотите, чтобы код запускался на set / get, это должен быть метод.

1
ответ дан 2 December 2019 в 05:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: