Are get and set functions popular with C++ programmers?

前端 未结 14 695
忘掉有多难
忘掉有多难 2020-11-28 23:12

I\'m from the world of C# originally, and I\'m learning C++. I\'ve been wondering about get and set functions in C++. In C# usage of these are quite popular, and tools like

相关标签:
14条回答
  • 2020-11-28 23:47

    The compiler will emit set_ and get_ if you define a property, so it's really just save some typing.

    This has been an interesting discussion. This is something from my favorite book "CLR via C#".

    Here is what I quoted.

    Personally, I don't like properties and I wish that they were not supported in the Microsoftm.NET Framework and its programming languages. The reason is because properties look like fields but they are methods. This has been known to cause a phenomenal amount of confu-sion. When a programmer sees code that appears to be accessing a field, there are many assumptions that the programmer makes that may not be true for a property. For example,

    • A property may be read-only or write-only; field access is always
      readable and writable. If you define
      a property, it is best to offer both
      get and set accessor methods.
    • A property method may throw an exception; field access never throws
      an exception.

    • A property cannot be passed as an out or ref parameter to a method; a field can.

    • A property method can take a long time to execute; field access always
      completes imme- diately. A common
      reason to use properties is to
      perform thread synchronization, which can stop the thread forever, and
      therefore, a property should not be
      used if thread synchro- nization is
      required. In that situation, a method is preferred. Also, if your class can be accessed remotely (for example,
      your class is derived from
      System.MashalByRefObject), calling
      the property method will be very
      slow, and therefore, a method is
      preferred to a property. In my
      opinion, classes derived from
      MarshalByRefObject should never use
      properties.

    • If called multiple times in a row, a property method may return
      a different value each time; a
      field returns the same value each
      time. The System.DateTime class has a read- only Now property that returns
      the current date and time. Each time you query this property, it will
      return a different value. This is a
      mistake, and Microsoft wishes that
      they could fix the class by making
      Now a method instead of a property.

    • A property method may cause observable side effects; field access never does. In other words, a user of a type should be able to set various
      properties defined by a type in any
      order he or she chooses without
      noticing any different behavior in
      the type.

    • A property method may require additional memory or return a
      reference to something that is not
      actually part of the object's state, so modifying the returned object has
      no effect on the original object;
      querying a field always returns a
      reference to an object that is
      guaranteed to be part of the original object's state. Working with a
      property that returns a copy can be
      very confusing to developers, and
      this characteristic is frequently not documented.
    0 讨论(0)
  • 2020-11-28 23:48

    There is no really strict convention on this, like there is in C# or Java. Many C++ programmers would just make the variable public an save themselves the trouble.

    As other answers have said, you shouldn't often need set, and to some extent, get methods.

    But if and when you do make them, there's no need to type more than necessary:

    class Foo
    {
    public:
        std::string Bar() const { return bar; }
        void Bar(const std::string& bar) { this->bar = bar; }
    private:
        std::string bar;
    };
    

    Declaring the functions inline in the class saves typing, and hints to the compiler that you'd like the functions inlined. And it's not much more typing than the C# equivalents. One thing to note is that I removed the get/set prefixes. Instead, we just have two Bar() overloads. That's fairly common in C++ (after all, if it doesn't take any arguments, we know it's the getter, and if it takes an argument, it's the setter. We don't need the name to tell us that), and it saves a bit more typing.

    0 讨论(0)
  • 2020-11-28 23:50

    If you use C++/CLI as your varient of C++, then it has native property support in the language, so you can use

    property String^ Name;
    

    This is the same as

    String Name{get;set;}
    

    in C#. If you need more precise control over the get/set methods then you can use

    property String^ Name
    {
       String^ get();
       void set(String^ newName);
    }
    

    in the header and

    String^ ClassName::Name::get()
    {
       return m_name;
    }
    
    void ClassName::Name::set(String^ newName)
    {
       m_name = newName;
    }
    

    in the .cpp file. I can't remember off hand, but I think you can have different access permissions for the get and set methods (public/private etc).

    Colin

    0 讨论(0)
  • 2020-11-28 23:54

    In your example:

    class Foo
    {
    public:
        const std::string GetBar(); // Should this be const, not sure?
    

    You probably mean this:

    std::string GetBar() const;
    

    Putting the const at the end means "This function doesn't modify the Foo instance it is called on", so in a way it marks it as a pure getter.

    Pure getters occur frequently in C++. An example in std::ostringstream is the str() function. The Standard library often follows a pattern of using the same function name for a pair of getter/setter functions - str being an example again.

    As to whether it's too much work to type out, and is it worth it - that seems an odd question! If you need to give clients access to some information, provide a getter. If you don't, then don't.

    0 讨论(0)
  • 2020-11-28 23:58

    [edit] It seems I need to emphasize that setters need to validate parameters and enforce invariants, so they are usually not as simple as they are here. [/edit]


    Not with all, because fo the extra typing. I tend to use them much more often now that Visual Assist gives me "encapsulate field".

    The legwork is not more if you implement just the default setters / getters inline in the class declaration (which I tend to do - more complex setters move to the body, though).

    Some notes:

    constness: Yes, the getter should be const. It is no use to make the return value const, though, if you return by value. For potentially complex return values you might want to use const & though:

    std::string const & GetBar() const { return bar; } 
    

    Setter chaining: Many developers like to modify the setter as such:

    Foo & SetBar(std::string const & bar) { this->bar = bar; return *this; }
    

    Which allows calling multiple setters as such:

    Foo foo;
    foo.SetBar("Hello").SetBaz("world!");
    

    It's not universally accepted as a good thing, though.

    __declspec(property): Visual C++ provides this non-standard extension so that callers can use property syntax again. This increases legwork in the class a bit, but makes caller code much friendlier looking.


    So, in conclusion, there's a little bit of more legwork, but a handful of decisions to make in C++. Typical ;)

    0 讨论(0)
  • 2020-11-28 23:59

    At the risk of being argumentative, I'll back an opposing point of view I first encountered while reading "Holub on Patterns". It was a point of view that was very challenging, but made sense to me upon reflection:

    Getters and Setters are Evil

    Use of getters and setters is in opposition to the fundamentals of object oriented design: Data abstraction and encapsulation. Overuse of getters and setters will make your code less agile and maintainable in the long run. They ultimately expose the underlying implementation of your class, locking implementation details into the interface of the class.

    Imagine your 'std::string Foo::bar' field needs to change from a std::string to another string class, that, say, is better optimized or supports a different character-set. You'll need to change the private data field, the getter, the setter, and all the client code of this class that calls these getters and setters.

    Rather than design your classes to "provide data" and "receive data", design them to "perform operations" or "providide services". Ask yourself why you're writing a "GetBar" function. What are you doing with that data? Perhaps you're displaying that data on or doing some processing on it. Is this process better exposed as a method of Foo?

    This not to say that getters and setters don't have their purpose. In C# I believe the fundamental reason for their use is to interface with the Visual Studio GUI-design IDE, but if you find yourself writing them in C++, it's probably best to take a step back, look at your design, and see if something is missing.

    I'll try to mock-up an example to illustrate.

    // A class that represents a user's bank account
    class Account {
      private:
        int balance_; // in cents, lets say 
      public:
        const int& GetBalance() { return balance_; }
        void SetBalance(int b) { balance_ = b; }
    };
    
    class Deposit {
      private:
        int ammount_;
      public:
        const int& GetAmount() { return ammount_; }
        void SetAmmount(int a) { _balance = a; }
    };
    
    void DoStuffWithAccount () {
      Account a;
      // print account balance
      int balance = a.GetBalance();
      std::cout << balance;
    
      // deposit some money into account
      Deposit d(10000);
      a.SetBalance( a.GetBalance() + d.GetValue());
    }
    

    It doesn't take very long to see that this is very poorly designed.

    1. Integers are an awful currency datatype
    2. A Deposit should be a function of the Account

    The getters and setters make it more difficult to fix the problems, since the client code DoStuffWithAccount is now bound to the data-type we used to implement the account balance.

    So, lets make a pass on this code and see what we can improve

    // A class that represents a user's bank account
    class Account {
      private:
        float balance_;
      public:
        void Deposit(float b) { balance_ += b; }
        void Withdraw(float w) { balance_ -= w; }
        void DisplayDeposit(std::ostream &o) { o << balance_; }
    };
    
    void DoStuffWithAccount () {
      Account a;
      // print account balance
      a.DisplayBalance(std::cout);
    
      // deposit some money into account
      float depositAmt = 1000.00;
      a.Deposit(depositAmt);
      a.DisplayBalance(std::cout);
    }
    

    The 'float' is a step in the right direction. Granted, you could have changed the internal type to 'float' and still supported the getter/setter idiom:

    class Account {
      private:
        // int balance_; // old implementation
        float balance_; 
      public:
        // support the old interface
        const int& GetBalance() { return (int) balance_; }
        void SetBalance(int b) { balance_ = b; }
        // provide a new interface for the float type
        const float& GetBalance() { return balance_; } // not legal! how to expose getter for float as well as int??
        void SetBalance(float b) { balance_ = b; }
    };
    

    but it doesn't take long to realize that the getter/setter arrangement is doubling your workload and complicating matters as you need to support both the code that used ints and the new code that will use floats. The Deposit function makes it a bit easier to expand the range of types for depositing.

    An Account-like class is probably not the best example, since "getting" the account balance is a natural operation for an Account. The overall point, though, is that you must be careful with getters and setters. Do not get into the habit of writing getters and setters for every data-member. It is quite easy to expose and lock yourself into an implementation if you are not careful.

    0 讨论(0)
提交回复
热议问题