In C++ I Cannot Grasp Pointers and Classes

Why should I use a pointer rather than the object itself?

It's very unfortunate that you see dynamic allocation so often. That just shows how many bad C++ programmers there are.

In a sense, you have two questions bundled up into one. The first is when should we use dynamic allocation (using new)? The second is when should we use pointers?

The important take-home message is that you should always use the appropriate tool for the job. In almost all situations, there is something more appropriate and safer than performing manual dynamic allocation and/or using raw pointers.

Dynamic allocation

In your question, you've demonstrated two ways of creating an object. The main difference is the storage duration of the object. When doing Object myObject; within a block, the object is created with automatic storage duration, which means it will be destroyed automatically when it goes out of scope. When you do new Object(), the object has dynamic storage duration, which means it stays alive until you explicitly delete it. You should only use dynamic storage duration when you need it.
That is, you should always prefer creating objects with automatic storage duration when you can.

The main two situations in which you might require dynamic allocation:

  1. You need the object to outlive the current scope - that specific object at that specific memory location, not a copy of it. If you're okay with copying/moving the object (most of the time you should be), you should prefer an automatic object.
  2. You need to allocate a lot of memory, which may easily fill up the stack. It would be nice if we didn't have to concern ourselves with this (most of the time you shouldn't have to), as it's really outside the purview of C++, but unfortunately, we have to deal with the reality of the systems we're developing for.

When you do absolutely require dynamic allocation, you should encapsulate it in a smart pointer or some other type that performs RAII (like the standard containers). Smart pointers provide ownership semantics of dynamically allocated objects. Take a look at std::unique_ptr and std::shared_ptr, for example. If you use them appropriately, you can almost entirely avoid performing your own memory management (see the Rule of Zero).

Pointers

However, there are other more general uses for raw pointers beyond dynamic allocation, but most have alternatives that you should prefer. As before, always prefer the alternatives unless you really need pointers.

  1. You need reference semantics. Sometimes you want to pass an object using a pointer (regardless of how it was allocated) because you want the function to which you're passing it to have access that that specific object (not a copy of it). However, in most situations, you should prefer reference types to pointers, because this is specifically what they're designed for. Note this is not necessarily about extending the lifetime of the object beyond the current scope, as in situation 1 above. As before, if you're okay with passing a copy of the object, you don't need reference semantics.

  2. You need polymorphism. You can only call functions polymorphically (that is, according to the dynamic type of an object) through a pointer or reference to the object. If that's the behavior you need, then you need to use pointers or references. Again, references should be preferred.

  3. You want to represent that an object is optional by allowing a nullptr to be passed when the object is being omitted. If it's an argument, you should prefer to use default arguments or function overloads. Otherwise, you should preferably use a type that encapsulates this behavior, such as std::optional (introduced in C++17 - with earlier C++ standards, use boost::optional).

  4. You want to decouple compilation units to improve compilation time. The useful property of a pointer is that you only require a forward declaration of the pointed-to type (to actually use the object, you'll need a definition). This allows you to decouple parts of your compilation process, which may significantly improve compilation time. See the Pimpl idiom.

  5. You need to interface with a C library or a C-style library. At this point, you're forced to use raw pointers. The best thing you can do is make sure you only let your raw pointers loose at the last possible moment. You can get a raw pointer from a smart pointer, for example, by using its get member function. If a library performs some allocation for you which it expects you to deallocate via a handle, you can often wrap the handle up in a smart pointer with a custom deleter that will deallocate the object appropriately.

What do people find difficult about C pointers?

I suspect people are going a bit too deep in their answers. An understanding of scheduling, actual CPU operations, or assembly-level memory management isn't really required.

When I was teaching, I found the following holes in students' understanding to be the most common source of problems:

  1. Heap vs Stack storage. It is simply stunning how many people do not understand this, even in a general sense.
  2. Stack frames. Just the general concept of a dedicated section of the stack for local variables, along with the reason it's a 'stack'... details such as stashing the return location, exception handler details, and previous registers can safely be left till someone tries to build a compiler.
  3. "Memory is memory is memory" Casting just changes which versions of operators or how much room the compiler gives for a particular chunk of memory. You know you're dealing with this problem when people talk about "what (primitive) variable X really is".

Most of my students were able to understand a simplified drawing of a chunk of memory, generally the local variables section of the stack at the current scope. Generally giving explicit fictional addresses to the various locations helped.

I guess in summary, I'm saying that if you want to understand pointers, you have to understand variables, and what they actually are in modern architectures.

Is it allowed comparing the pointers on static class fields in static_assert?

Compile-time equality comparison of the pointers on static class members is allowed.

The problem with original code was only in GCC and due to old GCC bug 85428.

To workaround it, one can explicitly instantiate the templates as follows:

template<int N> struct C { 
static int x;
};

template<int N> int C<N>::x = 0;

template struct C<0>;
template struct C<1>;

int main() {
static_assert(&C<0>::x != &C<1>::x);
}

This program is now accepted by all 3 major compilers, demo: https://gcc.godbolt.org/z/Kss3x8GnW

Is Pointer-to- inner struct member forbidden?

Yes, it is forbidden. You are not the first to come up with this perfectly logical idea. In my opinion this is one of the obvious "bugs"/"omissions" in the specification of pointers-to-members in C++, but apparently the committee has no interest in developing the specification of pointers-to-members any further (as is the case with most of the "low-level" language features).

Note that everything necessary to implement the feature in already there, in the language. A pointer to a-data-member-of-a-member is in no way different from a pointer to an immediate data member. The only thing that's missing is the syntax to initialize such a pointer. However, the committee is apparently not interested in introducing such a syntax.

From the pure formal logic point of view, this should have been allowed in C++

struct Inner {
int i;
int j[10];
};

struct Outer {
int i;
int j[10];
Inner inner;
};

Outer o;
int Outer::*p;

p = &Outer::i; // OK
o.*p = 0; // sets `o.i` to 0

p = &Outer::inner.i; // ERROR, but should have been supported
o.*p = 0; // sets `o.inner.i` to 0

p = &Outer::j[0]; // ERROR, but should have been supported
o.*p = 0; // sets `o.j[0]` to 0
// This could have been used to implement something akin to "array type decay"
// for member pointers

p = &Outer::j[3]; // ERROR, but should have been supported
o.*p = 0; // sets `o.j[3]` to 0

p = &Outer::inner.j[5]; // ERROR, but should have been supported
o.*p = 0; // sets `o.inner.j[5]` to 0

A typical implementation of pointer-to-data-member is nothing more than just an byte-offset of the member from the beginning of the enclosing object. Since all members (immediate and members of members) are ultimately laid out sequentially in memory, members of members can also be identified by a specific offset value. This is what I mean when I say that the inner workings of this feature are already fully implemented, all that is needed is the initialization syntax.

In C language this functionality is emulated by explicit offsets obtained through the standard offsetof macro. And in C I can obtain offsetof(Outer, inner.i) and offsetof(Outer, j[2]). Unfortunately, this capability is not reflected in C++ pointers-to-data-members.

Best way to handle memory allocation in C?

Part of the confusion is that it is inherently more difficult in C. malloc and free are similar to new and delete: malloc allocates new memory, and returns a pointer to that memory. free makes that memory available again, so long as it's memory that was allocated using malloc. Otherwise, it just makes hash of some chunk of memory. It doesn't care.

The important thing with malloc/free is to decide on and consistently maintain a disciplined use. Here are some hints:

ALWAYS check the returned pointer from malloc for NULL

if((p = (char *) malloc(BUFSIZ)) == NULL ) {
/* then malloc failed do some error processing. */
}

For belt and suspenders safety, set a pointer to NULL after freeing it.

free(p);
p = NULL ;

try to malloc and free a chunk of memory within the same scope if possible:

 {  char * p ;
if((p = malloc(BUFSIZ)) == NULL) {
/* then malloc failed do some error processing. */
}

/* do your work. */

/* now you're done, free the memory */

free(p);
p = NULL ; /* belt-and suspenders */
}

When you can't, make it clear that what you're returning is malloc'ed memory, so the caller can free it.

 /* foo: do something good, returning ptr to malloc memory */
char * foo(int bar) {
return (char *) malloc(bar);
}


Arrow operator (->) usage in C

foo->bar is equivalent to (*foo).bar, i.e. it gets the member called bar from the struct that foo points to.

Java reference variables

import java.util.List;
import java.util.ArrayList;

class FunkyObject
{
/** a list of other FunkyObject that this object is linked to */

List<FunkyObject> referenceList = new ArrayList<FunkyObject>();

/** creates a link between this object and someObject */

public addRefrerence(FunkyObject someObject )
{
referenceList.add(a);
}
}

Pointers to class of root type in C#

You don't need to declare them as pointers, just as the type:

class person{
person mom;
person dad;
string Name;
string Birthdate;

// Though I would probably use ICollection<Person> or IList<person>
person[] children;

//...constructors, destructors, methods etc...
}

Since person is a reference type, that's all you need.

In general, pointers are not needed in c# (though you can use them in code marked as unsafe).



Related Topics



Leave a reply



Submit