1
struct Date {
  int d, m , y;

  void init_date(Date& d, int, int, int);
  void add_year(int n);
  void add_month(Date& d, int n);
  void add_day(Date& d, int n);
}

Date my_birthday;
void f()
{
  Date today;
  today.init(16, 10, 1996);
  my_birtday.init(30, 12, 1950);

  Date tomorrow = today;
  tomorrow.add_day(1);
  // ...
}

Question A: In the above snippet, isn't: Date tomorrow = today; wrong in the sense that there is no "copy constructor" provided within struct Date. I know there is a "default constructor" auto generated by the compiler - but I'm not sure what exactly it does or how exactly it works (it can call the default constructor of a member-class, but it doesn't initialize: int i; and stuff like that). Could someone clarify how the compiler's default constructor works?

Question B: We would have to insert something like this: const Date& Date(const Date& r); correct? But, the above constructor implies we pass it one argument (a reference to "today"). So, how does an "initialization": "Date tomorrow = today" translate to a function call: Date tomorrow(today); Is there any "magic" happening here??

Question C: What is the format of the operator, new? Stroustrup uses it in different ways but there's no clear listing of all its uses. So far I could find: new ; new Type[size]; new Type(size); new Type; Is this listed/given anywhere? Have I missed out any?

2 Answers2

2

A) The default copy constructor does a shallow copy. Since you only have POD types, it will act the way you expect it to work, so it will be correct.

B) const Date& Date(const Date& r); wrong. Data::Date is a constructor, it can't be const or have a return type. If you want to implement a copy constructor, just write Date(const Date& r), but in this case you don't need one.

Date tomorrow = today;

is the same as

Date tomorrow(today);

The copy constructor is called, not a function.

C) Big topic, google for the new keyword, I'm sure it's well documented. You use new to allocate memory in dynamic storage.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
1

A. The compiler will implicitly generate a copy constructor, copy-assignment operator and destructor if you don't declare these yourself, and a default constructor if you don't declare any constructors. Each of these will act recursively on each base class and member of your class.

Default-initialising a numeric type (such as int) does nothing, leaving it uninitialised. So after Date today;, the members of today have unspecified values.

So your class acts as if you'd defined the following:

// Destructor
~Date() {}

// Default constructor
Date() {}

// Copy constructor
Date(Date const & x) : d(x.d), m(x.m), y(x.y) {}

// Copy-assignment operator
Date & operator=(Date const & x) {
    d = x.d;
    m = x.m;
    y = x.y;
    return *this;
}

B. Is answered by the above - there is "magic" happening in the sense that the compiler is generating these functions for you.

C. Any decent C++ book, including the one you refer to in the question title, will tell you how new works. In general, new and delete are used when you need exact control over object lifetimes. Your specific cases:

  • new Type[size] creates an array of size objects, each default-initialised. This must be deleted using delete[].
  • new Type(size) creates a single object, initialising it with the value of size. This must be deleted using delete.
  • new Type creates a single object, default-initialised. Again, this must be deleted using delete.

There are some other standard forms:

  • new Type[size]() and new Type() will value-initialise, rather than default-initialise the objects; this means that numerical types will be initialised to zero, and pointers to null, rather than being left uninitialised.
  • new (std::nothrow) Type will give a null pointer, rather than throwing std::bad_alloc, if it fails.
  • new (pointer) Type will construct an object in the memory specified by pointer, rather than allocating memory. You mustn't delete this object; instead you must call its destructor (object->~Type()) to destroy it without trying to deallocate the memory. This is not something that you do very often.

You can also define your own forms, but that's far beyond the scope of this question.

You should avoid using new unless you genuinely need to control an object's lifetime independently of any other object or code block. When you do, it's a very good idea to use RAII techniques (especially smart pointers) to make sure they are correctly deleted; it can be difficult to track them manually.

Community
  • 1
  • 1
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644