I have written a customized hash map, whose value contains an std::vector. And it caused core dump in real environment. There is a simplified example below. There are three questions:
Why vector size doesn't change to zero when I called the destruct function ~Value()? Does this program really release the memory allocated by vector before when I call the destruct function? And when I gdb attaches to this program, it's __M_start and __M_finish pointer still point to the same address.
Add1 and Add2 both have memory problems. Add1 won't call the construct function of vector, when I push_back int* to the vector of Value which has been erase before, it might be added to an illegal address. When I want to use the Add2 function to call the copy construct function, it might cause segment fault. Why it is still not working with Add2?
How should I change my program if I still want to use my customized map instead of std::map?
#include <iostream>
#include <functional>
#include <cstdlib>
#include <stdio.h>
#include <memory>
#include <vector>
#include <algorithm>
#include<iterator>
#include <map>
#include <iterator>
#include <limits>
#include <set>
using namespace std;
class Value
{
public:
// Value()
// {
// }
// Value(const Value &obj): stArr(obj.stArr)
// {
// cout<<"Call copy construct"<<endl;
// }
vector<int*> stArr;
};
class CustomMap
{
public:
// normal way
void Add1(int key, const Value& val)
{
stArrVal[key] = val;
}
// placement new
void Add2(int key, const Value& val)
{
new (&stArrVal[key]) Value(val);
// stArrVal[key] = val;
}
// void Add2(int key, const Value& val)
// {
// new (&stArrVal[key]) Value(val);
// }
void Erase(int key)
{
stArrVal[key].~Value();
// stArrVal[key].stArr.clear();
cout<<stArrVal[key].stArr.size()<<endl;
}
void Search(int key, Value *&pstVal)
{
pstVal = &stArrVal[key];
}
Value stArrVal[100];
};
void PrintValAddress(Value *pVal)
{
for (const auto & val : pVal->stArr)
{
cout<<&val<<", ";
}
cout<<endl;
}
int main()
{
vector<int> arr;
arr.push_back(1);
arr.push_back(3);
std::map<int, Value> stlMap;
CustomMap customMap;
Value* value = new Value();
Value* value2 = new Value();
int a = 1;
int b = 2;
int c = 3;
value->stArr.push_back(&a);
value->stArr.push_back(&b);
value->stArr.push_back(&c);
value2->stArr.push_back(&a);
value2->stArr.push_back(&b);
value2->stArr.push_back(&c);
// value2->stArr.push_back(&d);
stlMap.insert(pair<int, Value>(1, *value));
Value &stlV = stlMap[1];
Value *pstCusV = NULL;
customMap.Add1(1, *value);
customMap.Add2(2, *value);
customMap.Search(1, pstCusV);
PrintValAddress(pstCusV);
customMap.Search(2, pstCusV);
PrintValAddress(pstCusV);
// Release
customMap.Erase(1);
customMap.Erase(2);
// this line will cause segment fault.
// int* arr2 = new int[1024*1024];
customMap.Search(2, pstCusV);
// 1. still size 3
PrintValAddress(pstCusV);
// int* arr1 = new int[10];
customMap.Add1(1, *value2);
customMap.Add2(2, *value2);
customMap.Search(1, pstCusV);
PrintValAddress(pstCusV);
customMap.Search(2, pstCusV);
PrintValAddress(pstCusV);
return 0;
}
Output:
0xa14090, 0xa14098, 0xa140a0,
0xa14160, 0xa14168, 0xa14170,
3
3
0xa14160, 0xa14168, 0xa14170,
0xa14090, 0xa14098, 0xa140a0,
0xa14160, 0xa14168, 0xa14170,
Thanks a lot!
Related
The project was to make our own set class that we went over, but to use smart pointers. I got all my functions to work without smart pointer, but now that I tried to use them I'm getting issues with creating a new node.
#include "cs19_compact_string_set.h"
#include <memory>
#include <queue>
#include <string>
#include <utility>
#include <vector>
#include <string>
namespace cs19 {
CompactStringSet::CompactStringSet() :root_{0}, num_strings_{0} {}
bool CompactStringSet::insert(const std::string& value) {
if (this->find(value)) return true;
auto cur = this->root_;
for (auto character : value) {
auto search = this->find_next(cur, character);
if (search) {
cur = search;
} else {
auto new_node = std::shared_ptr<CompactStringSet::Node>(character);
if (cur->child) {
cur = cur->child;
while (cur->sibling)
cur = cur->sibling;
cur->sibling = new_node;
} else {
cur->child = new_node;
}
cur = new_node;
}
}
if (!cur->terminal) {
++this->num_strings_;
cur->terminal = true;
}
return false;
}
std::shared_ptr<CompactStringSet::Node> CompactStringSet::find_next(const
std::shared_ptr<CompactStringSet::Node> base, char to_find) const {
if (base->child) {
if (base->child->letter == to_find)
return base->child;
auto sibling = base->child->sibling;
while (sibling) {
if (sibling->letter == to_find)
return sibling;
sibling = sibling->sibling;
}
}
return nullptr; // No match found
}
} // namespace cs19
In the file where the functions are being implemented I keep getting errors for trying to make a new shared_ptr with the value character. I've tried to change it in a few different ways. but can't solve the issue. The error keeps reading error: no matching function for call to ‘std::shared_ptrcs19::CompactStringSet::Node::shared_ptr(char&)’.
#ifndef CS19_COMPACT_STRING_SET_H_
#define CS19_COMPACT_STRING_SET_H_
#include <memory>
#include <queue>
#include <string>
#include <utility>
#include <vector>
#include <string>
namespace cs19 {
class CompactStringSet {
struct Node {
char letter; // each node stores a letter
bool terminal = false; // ... and is potentially the end of a string in the set
std::shared_ptr<Node> sibling = nullptr;
std::shared_ptr<Node> child = nullptr;
};
public:
CompactStringSet();
bool insert(const std::string& value);
bool find(const std::string& value) const;
bool end() const {
return false;
}
std::size_t size() const {
return this->num_strings_;
}
private:
std::shared_ptr<Node> root_{0};
std::size_t num_strings_ = 0;
std::shared_ptr<Node> find_next(const std::shared_ptr<Node> base, char to_find) const;};
} // namespace cs19
#endif
You are trying to create a new instance of CompactStringSet by giving it a character as a parameter in the constructor
auto new_node = std::shared_ptr<CompactStringSet::Node>(character);
But your structure doesn't take any parameters and doesn't have any constructor, it's what the error says.
So you should propably replace it by :
auto new_node = std::make_shared<CompactStringSet::Node>();
new_node->letter = character;
You should also use make_shared instead of shared_ptr when you want to create a new shared_ptr
You code fails because you are trying to invoke a non existent function here
auto new_node = std::shared_ptr<CompactStringSet::Node>(character);
Seems like you somehow expect this to create a new node, assign the character to its 'letter' member and create a shared_ptr pointing at the new node. This is wishful thinking, no such function exisits - hence
no matching function for call to ‘std::shared_ptrcs19::CompactStringSet::Node::shared_ptr(char&)’.
You need (since you have no constructor for 'node' that accepts a character argument)
auto new_nodew = std::make_shared<CompactStringSet::Node>();
new_node->letter = character;
I need to copy the contents of a std::list into an array, wherein the array is struct of array. Below is the code implementation of it.
#include <iostream>
#include <string>
using namespace std;
typedef struct
{
int height;
int width;
int length;
}dimensions;
GetDimensions(list<std::string>, *int); // Function that copies the content of list to array passed as second parameter
int main()
{
dimensions cuboid[10];
int plane[10];
list<std::string> planeList = GetList();//Function that returns list of elements
list<std::string> dimensionList = GetList();
GetDimensions(planeList,&plane);//This is fine, as it is a simple array
GetDimensions(dimensionList,&cuboid.height);//Trouble in implementation of this usecase, for cuboid.height, cuboid.width and cuboid.height.
return 0;
}
GetDimensions(list<std::string>dimensionList, int* dimensionParams)
{
int i=0;
for(list<std::string>::iterator it = dimensionList.begin(); it != dimensionList.end(); ++it)
{
dimensionParams[i] = stoi(*it);
i++;
}
}
Here, I need GetDimensions() function to copy the list (passed as first parameter) to array (second parameter). The implemented function works well for simple array plane. But how to pass the array of struct as parameter to the function ?
I will be getting the std::list as cuboid.height, cuboid.width and cuboid.length. So the function has to copy the contents of list from cuboid[0].height to cuboid[i].height respectively. Is there any specific function to copy the content directly?
Use std::array 's instead. Then your problem can be reduced to passing two different types of arrays to a single function.
This can be solved
either by good old function overloads
or in c++17 function template with
if-constexpr.
Following is an example code with templated function with if-constexpr (See live online)
#include <iostream>
#include <string>
#include <list>
#include <array>
#include <type_traits> // std::is_same_v
struct dimensions // no need to typedef here
{
int height;
int width;
int length;
};
template<typename T>
void GetDimensions(const list<std::string>& dimensionList, T& dimensionParams)
^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //---> pass list by const-ref as the values are non-modifying
{
int i{0};
if constexpr (std::is_same_v<std::array<int, 10>, T>)
{
for(const std::string& str: dimensionList) dimensionParams[i++] = std::stoi(str);
}
else
{
for(const std::string& str: dimensionList) dimensionParams[i++].height = std::stoi(str);
}
}
int main()
{
std::array<dimensions, 10> cuboid; // use std::array instead of VLA
std::array<int, 10> plane;
std::list<std::string> planeList{"1", "2"}; // some list
std::list<std::string> dimensionList{"1", "2"};
GetDimensions(planeList, plane);
GetDimensions(dimensionList, cuboid);
return 0;
}
Also note that:
You have not specified the return type of GetDimensions function.
You probably want to return void there.
in C++ you do not need to use typedef alias for struct { ... }.
last but not least, do not practice with using namespace std;
You can do this with boost::transform_iterator.
#include <iostream>
#include <string>
#include <algorithm>
#include <functional>
#include <boost/iterator/transform_iterator.hpp>
struct dimensions {
int height;
int width;
int length;
};
template <typename OutputIt>
void GetDimensions(std::list<std::string> dimensionList, OutputIt dimensionParams)
{
// N.b. taking the address of a standard library function is undefined, so wrap in a lambda
auto stoi = [](std::string s){ return std::stoi(s); };
std::copy(boost::make_transform_iterator(dimensionList.begin(), stoi),
boost::make_transform_iterator(dimensionList.end(), stoi),
dimensionParams);
}
int main() {
dimensions cuboid[10];
int plane[10];
std::list<std::string> planeList = GetList();
std::list<std::string> heightList = GetList();
std::list<std::string> widthList = GetList();
std::list<std::string> lengthList = GetList();
GetDimensions(planeList, plane);
GetDimensions(heightList,
boost::make_transform_iterator(cuboid, std::mem_fn(&dimensions::height)));
GetDimensions(widthList,
boost::make_transform_iterator(cuboid, std::mem_fn(&dimensions::width)));
GetDimensions(lengthList,
boost::make_transform_iterator(cuboid, std::mem_fn(&dimensions::length)));
return 0;
}
I have 2 classes.
First Class - Midgam - The constructor has the following line:
midgam = new Vector[20];
The second class - Vector - where I create an array named array.
The program works great just that I have a little problem.
At the end of the program I try to print in alphabetical order, I use the BubbleSort sorting. The sorting works fine but something in the Swap function stops.
This is how it looks:
void Midgam::Swap(Vector *xp, Vector *yp) {
Vector temp = *xp;
cout << temp.getName() << endl;
*xp = *yp;
*yp = temp;
}
void Midgam::bubbleSort() {
int i, j;
for (i = 0; i < iterator - 1; i++) {
for (j = 0; j < iterator - i - 1; j++) {
if (midgam[j].getName().compare(midgam[j+1].getName()) > 0) {
Swap(&midgam[j], &midgam[j+1]);
}
}
}
}
I work with Visual Studio, the program stops and the program shows me the following code snippet in the Vector class:
Vector::~Vector() {
if (array)
delete[] array;
}
full definitions of Midgam:
#include <iostream>
#include <string>
#include "Vector.h"
using namespace std;
#ifndef MIDGAM_H_
#define MIDGAM_H_
class Midgam {
private:
int boxNum;
int maxParties;
int iterator;
Vector *midgam;
public:
Midgam(int num_of_boxes, int num_of_parties);
virtual ~Midgam();
void Start();
void Menurmal();
void SumOfEzor();
double SumOfParty(string name);
int SumAllVotes();
void AddParty();
void Swap(Vector *xp, Vector *yp);
void bubbleSort();
void Histograma();
void PrintStars(int num);
int FindPartyByName(string party);
void PrintAll();
};
#endif /* MIDGAM_H_ */
full definitions of Vector:
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
#ifndef VECTOR_H_
#define VECTOR_H_
class Vector {
private:
string name;
int size;
unsigned int *array;
bool Bool;
public:
Vector(string name, int size);
Vector();
Vector & operator=(const Vector &);
virtual ~Vector();
bool StringToArray(string str);
bool getBool();
string getName();
unsigned int getAddress();
int getSize();
unsigned int getValueFromArray(int index);
double sumOfArray();
void PrintArray();
};
#endif /* VECTOR_H_ */
Does anyone know why it does not work? Thank you
Your Vector lacks a proper copy constructor.
Vector temp = *xp;
//NOT EQUAL TO:
//Vector temp;
//temp=*xp;
The above statement won't call operator=(const Vector &) even though there's an equal sign. The following line is correct and equivalent:
Vector temp(*xp);
The reason is that this is a copy initialization - temp is created and so a constructor must be called - in particular the copy constructor Vector(const Vector &). Which you did not explicitly declared and so a default one was used.
Then a shallow copy is made, temp and *xp then share the same array and when both their destructors get called the second one will try to delete already deleted memory - undefined behavior which triggers Visual Studio's debugger (at least in debug mode).
The solution is to do a proper deep copy - create a new array and copy its contents:
#include <algorithm> #Contains std::copy_n
Vector::Vector(const Vector& other)
{
name=other.name;
size=other.size;
//Creates a new array
array= new unsigned int[size];
//Copies the array contents
std::copy_n(other.array,size,array);
Boo=other.Bool;
}
Also this is a prime example of why not to use raw memory. I get that you are implementing custom vector and don't want to use std::vector for the array but at least use std::unique_ptr. If you would have just done that you wouldn't have to ask this question in the first place as the compiler would have complained and the debugger wouldn't have to do the compiler's job.
I am stuck on the output member function of the class. I have no idea how to create it and just simply couting it does not seem to work. also any other advice would be great. thanks in advance
here's the code:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class StringSet
{
public:
StringSet(vector<string> str);
void add(string s);
void remove(int i);
void clear();
int length();
void output(ostream& outs);
private:
vector<string> strarr;
};
StringSet::StringSet(vector<string> str)
{
for(int k =0;k<str.size();k++)
{
strarr.push_back(str[k]);
}
}
void StringSet::add(string s)
{
strarr.push_back(s);
}
void StringSet::remove(int i)
{
strarr.erase (strarr.begin()+(i-1));
}
void StringSet::clear()
{
strarr.erase(strarr.begin(),strarr.end());
}
int StringSet::length()
{
return strarr.size();
}
void StringSet::output()
{
}
int main()
{
vector<string> vstr;
string s;
for(int i=0;i<10;i++)
{
cout<<"enter a string: ";
cin>>s;
vstr.push_back(s);
}
StringSet* strset=new StringSet(vstr);
strset.length();
strset.add("hello");
strset.remove(3);
strset.empty();
return 0;
}
Ok, you should begin by solving some errors in your code:
You use a pointer to StringSet and after that you are trying to access the member-functions with the . operator instead of the ->. Anyway, do you really need to allocated your object dynamically ?
StringSet strset(vstr); // No need to allocated dynamically your object
After that, you are calling an empty() method which does not exist...
Also if you stay on dynamic allocation, don't forget to deallocated your memory :
StringSet* strset = new StringSet(vstr);
// ...
delete strset; // <- Important
Finally, I think that your function output should write in the stream the content of your vector, you can do it that way :
#include <algorithm> // For std::copy
#include <iterator> // std::ostream_iterator
void StringSet::output( ostream& outs )
// ^^^^^^^^^^^^^ don't forget the arguments during the definition
{
std::copy(strarr.begin(), strarr.end(), std::ostream_iterator<string>(outs, "\n"));
}
HERE is a live example of your code fixed.
I would suggest you to understan how class works : http://www.cplusplus.com/doc/tutorial/classes/
If your output function is going to print the state of the StringSet object, you may implement is like this:
#include<iterator> //std::ostream_iterator
#include<algorithm> //std::copy
void StringSet::output(ostream& outs)
{
std::copy(starr.begin(), starr.end(), std::ostream_iterator<string>(outs, "\n"));
}
I was unsure about whether STL containers are entirely copied when passed. First, it worked (so the "fluttershy" element didn't get added, that was good). Then I wanted to track the construction and destruction of entries....
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
using namespace std;
int nextid = 0;
class Entry {
public:
string data;
int myid;
Entry(string in) {
data = in;
myid = nextid;
nextid++;
printf("Entry%02d\n", myid);
}
~Entry() { printf("~Entry%02d\n", myid); }
};
class Meep {
public:
vector<Entry> stuff;
};
void think(Meep m) {
m.stuff.push_back(Entry(string("fluttershy")));
}
int main() {
Meep a;
a.stuff.push_back(Entry(string("applejack")));
think(a);
vector<Entry>::iterator it;
int i = 0;
for (it=a.stuff.begin(); it!=a.stuff.end(); it++) {
printf("a.stuff[%d] = %s\n", i, (*it).data.c_str());
i++;
}
return 0;
}
Produces the following unexpected output( http://ideone.com/FK2Pbp ):
Entry00
~Entry00
Entry01
~Entry00
~Entry01
~Entry00
~Entry01
a.stuff[0] = applejack
~Entry00
That a has only one element is expected, that is not the question. What seriously confuses me is how can one entry be destructed several times ?
What you're seeing is the destruction of temporary instances.
a.stuff.push_back(Entry(string("applejack")));
This line creates a temporary instance which is then copied to another new instance in the container. Then the temporary is destroyed. The instance in the container is destroyed when the entry is removed or the container is destroyed.