开发者

What are all the member-functions created by compiler for a class? Does that happen all the time?

开发者 https://www.devze.com 2023-01-16 07:50 出处:网络
What are all the member-functions created by compiler for a class? Does that happen all the time? like destructor.

What are all the member-functions created by compiler for a class? Does that happen all the time? like destructor. My concern is whether it is created for all the classes, and why is default constructor n开发者_如何转开发eeded?


C++98/03

If they are needed,

  1. the compiler will generate a default constructor for you unless you declare any constructor of your own.
  2. the compiler will generate a copy constructor for you unless you declare your own.
  3. the compiler will generate a copy assignment operator for you unless you declare your own.
  4. the compiler will generate a destructor for you unless you declare your own.

As Péter said in a helpful comment, all those are only generated by the compiler when they are needed. (The difference is that, when the compiler cannot create them, that's Ok as long as they aren't used.)


C++11

C++11 adds the following rules, which are also true for C++14 (credits to towi, see this comment):

  • The compiler generates the move constructor if
    • there is no user-declared copy constructor, and
    • there is no user-declared copy assignment operator, and
    • there is no user-declared move assignment operator and
    • there is no user-declared destructor,
    • it is not marked deleted,
    • and all members and bases are moveable.
  • Similarly for move assignment operator, it is generated if
    • there is no user-declared copy constructor, and
    • there is no user-declared copy assignment operator, and
    • there is no user-declared move constructor and
    • there is no user-declared destructor,
    • it is not marked deleted,
    • and all members and bases are moveable.

Note that these rules are a bit more elaborate than the C++03 rules and make more sense in practice.

For an easier understanding of what is what in the above:

class Thing {
public:
    Thing();                        // default constructor
    Thing(const Thing&);            // copy c'tor
    Thing& operator=(const Thing&); // copy-assign
    ~Thing();                       // d'tor
    // C++11:
    Thing(Thing&&);                 // move c'tor
    Thing& operator=(Thing&&);      // move-assign
};

Further reading: if you are a C++-beginner consider a design that does not require you to implement any of five a.k.a The Rule Of Zero originally from an article written by Martinho Fernandes.


Do you mean 'defined' by 'created'?

$12.1 - "The default constructor (12.1), copy constructor and copy assignment operator (12.8), and destructor (12.4) are special member functions.

If 'created' means 'defined' then, here are the important parts from the C++ Standard.

-An implicitly-declared default constructor for a class is implicitly defined when it is used to create an object of its class type (1.8).

-If a class has no user-declared destructor, a destructor is declared implicitly. An implicitly-declared destructor is implicitly defined when it is used to destroy an object of its class type.

-If the class definition does not explicitly declare a copy constructor, one is declared implicitly. An implicitly-declared copy constructor is implicitly defined if it is used to initialize an object of its class type from a copy of an object of its class type or of a class type derived from its class type).

-If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. An implicitly-declared copy assignment operator is implicitly defined when an object of its class type is assigned a value of its class type or a value of a class type derived from its class type.


C++17 N4659 standard draft

https://github.com/cplusplus/draft/blob/master/papers/n4659.pdf 6.1 "Declarations and definitions" has a note which likely summarizes all of them:

3 [ Note: In some circumstances, C ++ implementations implicitly define the default constructor (15.1), copy constructor (15.8), move constructor (15.8), copy assignment operator (15.8), move assignment operator (15.8), or destructor (15.4) member functions. — end note ] [ Example: Given

#include <string>

struct C {
  std::string s;         // std::string is the standard library class (Clause 24)
};

int main() {
  C a;
  C b = a;
  b = a;
}

the implementation will implicitly define functions to make the definition of C equivalent to

struct C {
  std::string s;
  C() : s() { }
  C(const C& x): s(x.s) { }
  C(C&& x): s(static_cast<std::string&&>(x.s)) { }
  // : s(std::move(x.s)) { }
  C& operator=(const C& x) { s = x.s; return *this; }
  C& operator=(C&& x) { s = static_cast<std::string&&>(x.s); return *this; }
  // { s = std::move(x.s); return *this; }
  ~ C() { }
};

— end example ]

The conditions under which those are declared are explained at: Conditions for automatic generation of default/copy/move ctor and copy/move assignment operator?

A cool way to ensure that something has a default is to try and make it use = default as explained at: What does "default" mean after a class' function declaration?

The example below does that, and also exercises all the implicitly defined functions.

#include <cassert>
#include <string>

struct Default {
    int i;
    Default()                          = default;
    Default(const Default&)            = default;
    Default& operator=(Default&)       = default;
    Default& operator=(const Default&) = default;
    Default(Default&&)                 = default;
    Default& operator=(Default&&)      = default;
    ~Default()                         = default;
};

struct Instrument {
    int i;
    static std::string last_call;
    Instrument()                             { last_call = "ctor"; }
    Instrument(const Instrument&)            { last_call = "copy ctor"; }
    Instrument& operator=(Instrument&)       { last_call = "copy assign"; return *this; }
    Instrument& operator=(const Instrument&) { last_call = "copy assign const"; return *this; }
    Instrument(Instrument&&)                 { last_call = "move ctor";  }
    Instrument& operator=(Instrument&&)      { last_call = "move assign"; return *this; }
    ~Instrument()                            { last_call = "dtor"; }
};
std::string Instrument::last_call;

int main() {
    // See what the default constructors are doing.
    {
        // Default constructor.
        Default ctor;
        // i is uninitialized.
        // std::cout << ctor.i << std::endl;
        ctor.i = 1;

        // Copy constructor.
        Default copy_ctor(ctor);
        assert(copy_ctor.i = 1);

        // Copy assignment.
        Default copy_assign;
        copy_assign = ctor;
        assert(copy_assign.i = 1);

        // Copy assignment const.
        const Default const_ctor(ctor);
        Default copy_assign_const;
        copy_assign_const = const_ctor;
        assert(copy_assign_const.i == 1);

        // Move constructor.
        Default move_ctor(std::move(ctor));
        assert(move_ctor.i == 1);

        // Move assignment.
        Default move_assign;
        move_assign = std::move(ctor);
        assert(move_assign.i == 1);
    }

    // Check that the constructors are called by these calls.
    {
        // Default constructor.
        Instrument ctor;
        assert(Instrument::last_call == "ctor");

        // Copy constructor.
        Instrument copy_ctor(ctor);
        assert(Instrument::last_call == "copy ctor");

        // Copy assignment.
        copy_ctor = ctor;
        assert(Instrument::last_call == "copy assign");

        // Copy assignment const.
        const Instrument const_ctor(ctor);
        Instrument copy_assign_const;
        copy_assign_const = const_ctor;
        assert(Instrument::last_call == "copy assign const");

        // Move constructor.
        Instrument move_ctor(std::move(ctor));
        assert(Instrument::last_call == "move ctor");

        // Move assignment.
        Instrument move_assign;
        move_assign = std::move(ctor);
        assert(Instrument::last_call == "move assign");

        // Destructor.
        {
            Instrument dtor;
        }
        assert(Instrument::last_call == "dtor");
    }
}

GitHub upstream.

Tested with GCC 7.3.0:

g++ -std=c++11 implicitly_defined.cpp


By default, if not implemented by the user, the compiler add some member functions to the class. Those are called the big four :

  • default constructor
  • copy constructor
  • copy operator (assignment)
  • destructor

Depending on the types of the members and which member function listed you provide yourself, those will not all be generated.


Other answers have told you what's created, and that the compiler may only generate them if used.

My concern is whether it is created for all the classes...

Why concerned? Thinking it's creating unwanted code in the executable? Unlikely, but you can check easily enough with your environment.

Or perhaps your concern was that it might not create a constructor when you want one? Nothing to worry about... they're always created if needed and not provided by the user.

...and why is default constructor needed?

Because classes may have objects inside them with their own destructors that need to be systematically invoked. For example, given...

struct X
{
    std::string a;
    std::string b;
};

...the default destructor makes sure the destructors for a and b run.

0

精彩评论

暂无评论...
验证码 换一张
取 消