C++: question on placement new in a union using Stroustrup example - c++

I'm using the following example from Stroustrup C++ 4th Ed. Page 218. My question is regarding the destructor.
Questions:
How does placement new(&s) string{ss} allocate room for the new string when it's my understanding that union size is fixed to the largest member? Is string s not a 0 element string? If so, how does the placement new create a larger string if there is not enough space in the union?
#include <iostream>
#include <string>
using namespace std;
class Entry2 { // two alternative representations represented as a union
private:
enum class Tag { number, text };
Tag type; // discriminant
union { // representation
int i;
string s; // string has default constructor, copy operations, and destructor
};
public:
struct Bad_entry { }; // used for exceptions
string name;
Entry2(int n) : type{Tag::number}, i{n} { };
Entry2(string ss) : type{Tag::number} { new(&s) string{ss}; };
~Entry2();
Entry2& operator=(const Entry2&); Entry2(const Entry2&);
// ...
int number() const; string text() const;
void set_number(int n);
void set_text(const string&); // ...
};
Entry2::~Entry2()
{
if (type==Tag::text)
s.~string();
}
int Entry2::number() const
{
if (type!=Tag::number) throw Bad_entry{};
return i;
}
string Entry2::text() const
{
if (type!=Tag::text) throw Bad_entry{};
return s;
}
void Entry2::set_number(int n)
{
if (type==Tag::text) {
s.~string();
type = Tag::number;
}
i = n;
}
void Entry2::set_text(const string& ss)
{
if (type==Tag::text)
s = ss;
else {
new(&s) string{ss};
type = Tag::text;
}
}
Entry2& Entry2::operator=(const Entry2& e)
{
if (type==Tag::text && e.type==Tag::text) {
s = e.s; // usual string assignment
return *this;
}
if (type==Tag::text)
s.~string(); // explicit destroy (ยง11.2.4)
switch (e.type) {
case Tag::number:
i = e.i;
break;
case Tag::text:
new(&s) string{e.s};
type = e.type;
}
return *this;
}
int main(int argc, char *argv[])
{
Entry2 e0(0);
cout << e0.number() << endl;
try {
e0.text();
} catch (...) {
cout << "caught except" << endl;
}
e0.set_text("abcd");
cout << e0.text() << endl;
return 0;
}

No, the destructor should not always do this. Remember that in a union, only one of the fields is actually active at any one time. If the std::string member of the union isn't active, then calling its destructor would be a Bad Thing (cause undefined behavior) because there wasn't a string there to destroy. Instead, we instead only call the destructor on that std::string member if at some previous point in time we activated the std::string there.
Hope this helps!

Related

c++ How to initialize char in constructor

#include <iostream>
using namespace std;
class MyClass
{
private :
char str[848];
public :
MyClass()
{
}
MyClass(char a[])
{
str[848] = a[848];
}
MyClass operator () (char a[])
{
str[848] = a[848];
}
void myFunction(MyClass m)
{
}
void display()
{
cout << str[848];
}
};
int main()
{
MyClass m1; //MyClass has just one data member i.e. character array named str of size X
//where X is a constant integer and have value equal to your last 3 digit of arid number
MyClass m2("COVID-19") , m3("Mid2020");
m2.display(); //will display COVID-19
cout<<endl;
m2.myFunction(m3);
m2.display(); //now it will display Mid2020
cout<<endl;
m3.display(); //now it will display COVID-19
//if your array size is even then you will add myEvenFn() in class with empty body else add myOddFn()
return 0;
}
I cannot use string because I'm told not to, therefore, I need to know how I can make it such that it displays the desired output
How to initialize char array in constructor?
Use a loop to copy element by element:
MyClass(char a[])
{
//make sure that sizeof(a) <= to sizeof(str);
// you can not do sizeof(a) here, because it is
// not an array, it has been decayed to a pointer
for (int i = 0; i < sizeof(str); ++i) {
str[i] = a[i];
}
}
Use std::copy from <algorithm>
const int size = 848;
std::copy(a, a + size, str);
Prefer std::copy over strcpy, if you have to use strcpy, prefer strncpy instead. You can give size to it, so it can help prevent errors and buffer overflows.
MyClass(char a[])
{
strncpy(str, a, sizeof(str));
}
Use std::array from the library. It has various advantages, for e.g you can directly assign it like normal variables. Example:
std::array<char, 848> str = {/*some data*/};
std::array<char, 848> str1;
str1 = str;
To copy a string you have to use std::strcpy, not str[848] = a[848].
str[848] = a[848] copy only one element, but in your case it's a mistake, becasue your array has indexes [0, 847].
Try
class MyClass
{
private :
char str[848];
public :
MyClass()
{
}
MyClass(char a[])
{
std::strcpy(src, a);
}
MyClass operator () (char a[])
{
std::strcpy(src, a);
}
void myFunction(MyClass m)
{
}
void display()
{
cout << str;
}
};

C++ vector of struct allocated on stack

If we have a vector of struct pointer MyInfo* (allocated on heap). Then we can check vec[i] == NULL to know whether there is a struct in the vec[i], like this, if (vec[i] != NULL) //then do some processing
However, if we allocate MyInfo on stack instead of on heap, then we have vector<MyInfo> as shown below. I guess each vec[i] is initialized by the struct default constructor. How do you check whether vec[i] contains a non-empty struct similar to above NULL pointer case, like if (vec[i] contains valid struct) //then do some processing
My code is below
#include <iostream> // std::cout
#include <string>
#include <vector>
using namespace std;
struct MyInfo {
string name;
int age;
};
int main () {
vector<MyInfo> vec(5);
cout << "vec.size(): " << vec.size() << endl;
auto x = vec[0];
cout << x.name << endl; //this print "" empty string
cout << x.age << endl; //this print 0
return 0;
}
There are some options you can use. The first and easiest one, is to define a value to each (or for one) of your struct's variables, that will point that the struct is not initialized yet. In this case, age should be large or equal to 0, to be logicly straight. So, you can initialize it to -1, like this:
struct MyInfo {
string name;
int age = -1;
};
// Or
struct MyInfo {
string name;
int age;
MyInfo() : name(""), age(-1) {} // Use constructor
};
Now, in your main function, it will print in the age the value -1. Also, you can see the empty of the name variable as a sign for it too.
Another way might be using flag and get/set operations to indicate when the variables are initialize:
struct MyInfo {
private:
std::string _name;
int _age;
bool age_initialize = false;
bool name_initialize = false;
public:
void name(const std::string &name_p) { _name = name_p; name_initialize = true; }
void age(int age_p) { _age = age_p; age_initialize = true; }
void init(int age_p, const std::string &name_p) { age(age_p); name(name_p); }
bool is_initialize() { return name_initialize && age_initialize; }
int age() { return _age; }
std::string name() { return _name; }
};
int main() {
std::vector<MyInfo> vec(5);
std::cout << "vec.size(): " << vec.size() << std::endl;
auto x = vec[0];
std::cout << x.is_initialize() << std::endl; //this print 0
std::cout << x.name() << std::endl; //this print "" empty string
std::cout << x.age() << std::endl; //this print 0
return 0;
}
You can also throw an exception when calling int age() of std::string name() function, if those values are not initialize yet:
struct MyInfo {
private:
/* ... */
public:
/* ... */
int age() {
if (!age_initialize) throw std::runtime_error("Please initialize age first.");
return _age;
}
std::string name() {
if (!name_initialize) throw std::runtime_error("Please initialize name first.");
return _name;
}
};

Chaining constructor with const char * as argument gives unexpected results

I have this C++ class:
class Test
{
private:
string _string;
public:
Test()
{
}
Test(const char *s)
{
Test((string)s);
}
Test(string s)
{
_string = s;
}
operator const char *()
{
return _string.c_str();
}
operator string()
{
return _string;
}
};
If I use this code in main "1234" is printed to the console:
int main()
{
Test test = string("1234");
string s = test;
cout << s << endl;
return 0;
}
But with this, nothing is printed:
int main()
{
Test test = "1234"; // Only change
string s = test;
cout << s << endl;
return 0;
}
The only difference is which constructor is called. It apperas that the _string variable is a default string instance with the value "" but I don't see how that could have happend. I thought that since _string is on the stack, the assignment I do is safe.
This
Test(const char *s)
{
Test((string)s);
}
does not chain the constructors. It just creates a temporary object in the body of the function. What you need is:
Test(const char *s) : Test(string(s))
{
}

Expression: _BLOCK_TYPE_IS_VALID(pHead->nBlockUse) Error

This error occurs during run time, and I'm not sure what's causing it - the code looks correct to me.
#include <iostream>
#include <string>
using namespace std;
struct Room {
int d_noSeat;
bool d_hasProjector;
Room() = default;
Room(const Room& r);
};
class Event {
Room* d_room;
std::string d_name;
public:
Event();
Event(const Event& e);
~Event();
void set(Room r, const std::string& name);
void print();
};
Event::Event() : d_room(0), d_name("") {};
void Event::print() {
std::cout << "Event: " << d_name;
if (d_room != 0) {
std::cout << " in size " << d_room->d_noSeat;
if (d_room->d_hasProjector)
std::cout << " with";
else
std::cout << " without";
std::cout << " projector";
}
std::cout << std::endl;
return;
}
void printEvent(Event e) {
e.print();
return;
}
void Event::set(Room r, const std::string& name) {
d_room = &r;
d_name = name;
}
// Room shallow copy constructor
Room::Room(const Room& r) :
d_noSeat(r.d_noSeat),
d_hasProjector(r.d_hasProjector)
{ }
// Event deep copy constructor
Event::Event(const Event& e) :
d_name(e.d_name),
d_room(new Room(*e.d_room))
{ }
// Event destructor
Event::~Event()
{
delete[] d_room;
}
int main() {
const int noLect = 5;
Room r;
Event lectures[noLect];
for (int i = 0; i < noLect; ++i) {
r.d_noSeat = i + 1;
r.d_hasProjector != r.d_hasProjector;
lectures[i].set(r, "CSI2372");
lectures[i].print();
}
std::cout << "-------------------" << std::endl;
for (int i = 0; i < noLect; ++i) {
printEvent(lectures[i]);
}
return 0;
}
The error apparently occurs at line 52 (first line in the print() function). In addition to this, the printed text displays numbers that are very large and often negative. What is causing this?
Issue
void Event::set(Room r, const std::string& name)
{
d_room = &r;
// ^
d_name = name;
}
You are referencing to the temporary object: Room r passed by value, which is destroyed at the end of the scope: }.
Instead you must reallocate the member pointer:
d_room = new Room(r);
Why it went wrong
Because you are writing C-style code in C++ classes.
In C++ we tend to:
Avoid naked pointers, prefer smart pointers:
class Event
{
std::shared_ptr<Room> d_room;
...
Event::~Event() { /* no need to delete */ }
Use constructor overloading (instead of using set-like functions after construction):
Event(Room& r, const std::string& name):
d_room(new Room(r)),
d_name(name)
{}
Pass by reference:
void set(Room& r, const std::string& name);
Avoid raw arrays, use STL facilities instead:
std::vector<Event> lectures;
// or
std::array<Event, 5> lectures;
Another issue
r.d_hasProjector != r.d_hasProjector; // checks if r.d_hasProject is not itself
You probably want
r.d_hasProjector = !r.d_hasProjector;
Complete code: link
Also, here is a must-read link about advanced C++ stuff which, I believe, will be very useful to you: http://www.parashift.com/c++-faq/
Edit: I forgot about your question:
In addition to this, the printed text displays numbers that are very large and often negative. What is causing this?
Those numbers are garbage. Variables that are not explicitly initialized are not initialized at all. Memory is allocated but holds old information from previous program. It could contain anything. When you read from uninitialized variables, you'll get this garbage. You had a pointer which was pointing to a destroyed object. So the pointer was effectively uninitialized.
Your problem is here:
void Event::set(Room r, const std::string& name) {
d_room = &r;
d_name = name;
}
The &r takes the address of an object whose lifetime ends when the function returns, resulting in undefined behaviour when you later try to access it.
If you want to use pointers, you need to allocate them dynamically:
void Event::set(Room* r, const std::string& name) {
d_room = r;
d_name = name;
}
// ...
for (int i = 0; i < noLect; ++i) {
Room* r = new Room;
r->d_noSeat = i + 1;
r->d_hasProjector != r.d_hasProjector;
lectures[i].set(r, "CSI2372");
lectures[i].print();
}
// ...
But it doesn't look like you need pointers here, you should be able to have
Room d_room;
in the Event class.

Memory leak in code snippet

I'm trying to dust off my C++. I knocked together a simple program to find the Fibonacci sequence with memoization. There's a memory leak, and I can't seem to figure out why. The leak is reported in Fibonacci::setToFind.
Sorry for the long code chunk, but I couldn't figure out how to make a more minimal reproducible example.
#include <iostream>
class Fibonacci
{
public:
int m_valuefound;
int m_tofind;
long int *m_memo;
int findValue(int value){
if (m_memo[value] == 0) {
if (value == 0 || value == 1) {
m_memo[value] = 1;
} else {
m_memo[value] = findValue(value-1) + findValue(value-2);
}
}
return m_memo[value];
}
void setToFind(int value){
m_tofind = value;
m_memo = new long int[value];
std::fill_n(m_memo,value,0);
}
void solve(){
int value = m_tofind;
int result = findValue(value);
std::cout<< "Value is: " << result << std::endl;
}
~Fibonacci(){};
};
int main (int argc, char * const argv[]) {
std::cout << "Enter integer values until you'd like to quit. Enter 0 to quit:";
int user_ind=0;
// for testing non-interactivly
while(true){
for (user_ind=1; user_ind<45; user_ind++) {
Fibonacci *test = new Fibonacci;
test->setToFind(user_ind);
test->solve();
delete test;
}
}
return 0;
}
You never delete m_memo in the destructor of Fibonacci.
Since you're allocating m_memo as an array, you should delete with delete[] m_memo
Here is working code with a non-copyable Fibonacci class. Why don't
you allocate the memory in the constructor. Use RAII wherever possible
and remember The Rule of Five. Avoid all of this in the first place by
using std::vector.
#include <iostream>
class Fibonacci
{
public:
int m_valuefound;
int m_tofind;
long int *m_memo;
int findValue(int value){
if (m_memo[value] == 0) {
if (value == 0 || value == 1) {
m_memo[value] = 1;
} else {
m_memo[value] = findValue(value-1) + findValue(value-2);
}
}
return m_memo[value];
}
void setToFind(int value){
m_tofind = value;
m_memo = new long int[value];
std::fill_n(m_memo,value,0);
}
void solve(){
int value = m_tofind;
int result = findValue(value);
std::cout<< "Value is: " << result << std::endl;
}
// why don't you allocate in the constructor?
Fibonacci() : m_valuefound(0), m_tofind(0), m_memo(nullptr) {}
~Fibonacci() {
delete[] m_memo;
};
// make the class non-copyable
Fibonacci(const Fibonacci&) = delete;
const Fibonacci& operator=(const Fibonacci&) = delete;
/*
C++03 non-copyable emulation
private:
Fibonacci(const Fibonacci&);
const Fibonacci& operator=(const Fibonacci&);
*/
};
You are allocating m_memo in setToFind:
m_memo = new long int[value];
but your destructor does not have a delete [] m_memo. You should initialize m_memo in your constructor and make you class non-copyable by disabling your copy constructor and assignment operator using delete if using C++11:
Fibonacci(const Fibonacci&) = delete;
const Fibonacci& operator=(const Fibonacci&) = delete;
Otherwise you can make them private. If you used a container such as std::vector your life would be much simpler.
I suggest you use more the STL algorithms. Here's a code snippet with a rather not optimized functor but you can get the idea of the power of the STL:
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
class Fibonacci
{
public:
Fibonacci();
~Fibonacci() {}
int operator()();
private:
int n0_;
int n1_;
int n_;
};
Fibonacci::Fibonacci():n0_(0),n1_(1),n_(0)
{
}
int Fibonacci::operator()()
{
if(n_ > 1)
return (++n0_) + (++n1_);
else
return ++n_;
}
using namespace std;
int main()
{
Fibonacci func;
vector<int> v;
//generate 100 elements
generate_n(v.begin(),100,func);
//printing the values using a lambda expression
for_each(v.begin(),v.end(),[](const int val){cout << val << endl;});
return 0;
}
You can then apply the finding algorithm you want on the vector using find_if and defining your own functor.