When Should Functions Be Member Functions

When should functions be member functions?

Scott Meyers has advocated that non-member functions often improve encapsulation:

  • How Non-Member Functions Improve Encapsulation

Herb Sutter and Jim Hyslop also talk about this (citing Meyer's article) in "Self-Sufficient Headers"

  • http://www.ddj.com/cpp/184401705

These ideas have been republished (in more refined form) in the 3rd edition of Meyer's "Effective C++", "Item 23: Prefer non-member non-friend functions to member functions ", and Sutter/Alexandrescu's "C++ Coding Standards", "44 - Prefer writing nonmember nonfriend functions".

I think a lot of developers find this non-intuitive and maybe a little controversial.

How member functions are implemented in C++?

Looking at this site, just to pick a tutorial among others if you google the exact same question, you'll find this

Member functions are the functions, which have their declaration inside the class definition and works on the data members of the class. The definition of member functions can be inside or outside the definition of class.

If the member function is defined inside the class definition it can be defined directly, but if its defined outside the class, then we have to use the scope resolution :: operator along with class name alng with function name.

As you can see, a member function is a function which belongs to an instance of a class, having access to object's members and to pointer-to-self-object this

Of course, in your example you're accessing

m.func(4)

which is member function defined inside object instance of class MyCls whilst writing

func(m, 5);

you're passing an object instance of class MyCls to the function in the global scope (by value which is a thing you should study about as well if it's the way you want to act as) without using it, just to verify the coherence of your thoughts.

I suggest you to start with a good C++ tutorial to learn about the language's OOP paradigm.

What are the necessary special member functions that should to be declared?

The default constructor is a special case among these. If some member variables need an initial state that is not the default value of those members, write a default constructor.

The rest of these should really only be overwritten if you know that you need it.

These are some reasonable cases in which you might want to write custom versions of the listed functions:

  1. Your class has member variables that are pointers to dynamic memory that it has allocated.
  2. Your class has data structures as members that the user is not expected to pass completed versions of into the constructor.
  3. Your class has members that you do not want to use the default copy model for (e.g. you want a deep copy but the copy constructor or assignment would make a shallow copy).
  4. Your class has a static data member that should be changed when new instances of it are created.

Examples of each of these use cases include:

  1. STL vectors, which dynamically allocates its underlying array and deallocates in its destructor.
  2. STL lists, which copy data that is added to them onto list nodes, the internals of which are not exposed to the end-user.
  3. Any STL container class is a good example of this.
  4. Something where the number of things which need access to an expensive resource are needed. Similar to the design goal of a std::shared_ptr.

If one class meets too many of these requirements, you should probably consider changing your design to use more of the containers an utilities provided by the standard library. Especially if one class requires the fourth property and any of the other three. The responsibilities related to points 1-3 and point 4 should be handled by different classes. It is important to note that ownership of one large, expensive-to-create resource and ownership of many resources are different types of responsibility.

References:

  • Explaination of the Rule of Three/Five
  • A list of STL container classes

Is there any advantage to implementing functions as free functions rather than members in C++?

This answer may helps you : Operator overloading : member function vs. non-member function?. In general free functions are mandatory if you need to implement operators on classes you don't have access to code source (think about streams) or if left operand is not of class type (int for example). If you control the code of the class then you can freely use function members.

For your last question, no, function members are uniquely defined and an object internal table is used to point to them. Function members can be viewed as free functions with an hidden parameter that is a pointer to the object, i.e. o.f(a) is more or less the same as f(&o,a) with a prototype roughly like f(C *this,A a);.

C++ Member Functions vs Free Functions

The Interface Principle by Herb Sutter

For a class X, all functions, including free functions, that both

(a) "mention" X, and

(b) are "supplied with" X

are logically part of X, because they form part of the interface of X.

For in depth discussion read Namespaces and the Interface Principle by Herb Sutter.

EDIT

Actually, if you want to understand C++ go and read everything what Herb Sutter has written :)

What are the use cases of class member functions marked &&?

Ref-qualification was added in c++11. In general, propagation of the value-category is incredibly useful for generic programming!

Semantically, ref-qualifications on functions help to convey the intent; acting on lvalue references or rvalues -- which is analogous to const or volatile qualifications of functions. These also parallel the behavior of struct members, which propagate the qualifiers and categories.

A great example of this in practice is std::optional, which provides the std::optional::value() function, which propagates the value-category to the reference on extraction:

auto x = std::move(opt).value(); // retrieves a T&&

This is analogous to member-access with structs, where the value-category is propagated on access:

struct Data {
std::string value;
};

auto data = Data{};

auto string = std::move(data).value; // expression yields a std::string&&

In terms of generic composition, this massively simplifies cases where the input may be an lvalue or an rvalue. For example, consider the case of using forwarding references:

// Gets the internal value from 'optional'
template <typename Optional>
auto call(Optional&& opt) {
// will be lvalue or rvalue depending on what 'opt' resolves as
return std::forward<Optional>(opt).value();
}

Without ref-qualification, the only way to accomplish the above code would be to create two static branches -- either with if constexpr or tag-dispatch, or some other means. Something like:

template <typename Optional>
auto call(Optional&& opt) {
if constexpr (std::is_lvalue_reference_v<Optional>) {
return opt.value();
} else {
return std::move(opt.value());
}
}

On a technical level, rvalue-qualifications on functions provides the opportunity to optimize code with move-constructions and avoid copies in a semantically clear way.

Much like when you see a std::move(x) on a value, you are to expect that x is expiring; it's not unreasonable to expect that std::move(x).get_something() will cause x to do the same.

If you combine && overloads with const & overloads, then you can represent both immutable copying, and mutating movements in an API. Take, for example, the humble "Builder" pattern. Often, Builder pattern objects hold onto pieces of data that will be fed into the object on construction. This necessitates copies, whether shallow or deep, during construction. For large objects, this can be quite costly:

class Builder {
private:

// Will be copied during construction
expensive_data m_expensive_state;
...

public:

auto add_expensive_data(...) -> Builder&;
auto add_other_data(...) -> Builder&;
...

auto build() && -> ExpensiveObject {
// Move the expensive-state, which is cheaper.
return ExpensiveObject{std::move(m_expensive_state), ...}
}
auto build() const & -> ExpensiveObject
// Copies the expensive-state, whcih is costly
return ExpensiveObject{m_expensive_state, ...}
}
...
};

Without rvalue-qualifications, you are forced to make a choice on the implementation:

  1. Do destructive actions like moves in a non-const function, and just document the safety (and hope the API isn't called wrong), or
  2. Just copy everything, to be safe

With rvalue-qualifications, it becomes an optional feature of the caller, and it is clear from the authored code what the intent is -- without requiring documentation:

// Uses the data from 'builder'. May be costly and involves copies
auto inefficient = builder.build();

// Consumes the data from 'builder', but makes 'efficient's construction
// more efficient.
auto efficient = std::move(builder).build();

As an added benefit, static-analysis can often detect use-after-move cases, and so an accidental use of builder after the std::move can be better caught than simple documentation could.

When should I prefer non-member non-friend functions to member functions?

More generally, what are the rules of when to use which?

Here is what Scott Meyer's rules are (source):

Scott has an interesting article in print which advocates
that non-member non-friend functions improve encapsulation
for classes. He uses the following algorithm to determine
where a function f gets placed:

if (f needs to be virtual)
make f a member function of C;
else if (f is operator>> or operator<<)
{
make f a non-member function;
if (f needs access to non-public members of C)
make f a friend of C;
}
else if (f needs type conversions on its left-most argument)
{
make f a non-member function;
if (f needs access to non-public members of C)
make f a friend of C;
}
else if (f can be implemented via C's public interface)
make f a non-member function;
else
make f a member function of C;

His definition of encapsulation involves the number
of functions which are impacted when private data
members are changed.

Which pretty much sums it all up, and it is quite reasonable as well, in my opinion.

How should non-member functions be declared and defined in the .h file and cpp file?

non-member function or "free function" is a function that lives outside of a class.
To create a function like that you can just declare it outside of the class.

.h

Class C
{
int func1(){}; //member function declared and defined in header
int func2(); //member function declared in header
...
}

int func(){}; //free-function declared and defined in header file
int func2(); //free-function declared in header file

.cpp

int C::func2(){} //definition of member function
int func2(){} //definition of free-function
int func3(){} //free-function that is declared and defined in the source file. can only be used within this file

You can see that I used the same function names for both the member function and the free-functions, that is fine because they live in a different namespace.



Related Topics



Leave a reply



Submit