The new operator of C++ is based on dynamic memory allocation, for example via malloc (alternative implementations that use sbrk directly are also possible). The standard C library integrates a malloc implementation, but does not know, which memory to use. The sbrk function is used by the standard C library to acquire some memory from the underlying system. In a hosted environment the OS provides this function. But on a freestanding environment this function has to be defined by you. After all, you're the only one who knows what the memory layout should be.
Depending on the standard C library that you use, there are different options. I'm guessing that you're using newlib (very popular for embedded systems). In this case you could provide an alternative malloc implementation instead of providing the sbrk function and using the implementation of newlib. Following code should more or less work. This provides the reentrant version of malloc. From the top of my head, I'm not sure how the non-reentrant function definition or sbrk needs to look like.
#define HEAP_SIZE 512
uint8_t heap[HEAP_SIZE];
size_t heap_idx = 0;
// Not sure what this has to be called. Try some of the following:
// void * sbrk(int size) {
// void * _sbrk(int size) {
void * _malloc_r(struct _reent * re, size_t size) {
void * ret = NULL;
if (heap_idx + size < HEAP_SIZE)
{
ret = &heap[heap_idx];
heap_idx += size;
}
return ret;
}
There is also example code in the newlib manual that uses all RAM thats not statically used as heap. Also there are other syscalls (exit, close, read...) that might be useful to implement. See chapter 12 'syscalls' in the newlib manual. That should also take care of the other undefined symbols. If you can't provide these syscalls (e.g. you don't have a filesystem), you can't use certain functions. For example fopen needs some syscalls like open, close, fstat...
For a fully functioning system, you probably also want to provide implementations for free, calloc and realloc. Also I have to add the boilerplate warning: dynamic allocation in embedded systems is incredibly dangerous, if you don't know exactly what you're doing, you should probably stick to static allocations. That's also possible with C++. For example, you could instantiate your class like this:
CGPIO pGPIO();
In this case the object is allocated on the stack and will be destroyed as soon as the variable goes out of scope. Little bonus: if you don't use new, you probably don't have to add a sbrk or malloc implementation.