How to use an array of string in c++? - c++

I need to make an array of string where I can store either 3 or 5 words like {"banana, peach, pear"}, or {"orange", "pear", "silantro", "ginger", "mandarine"}.
I'm confused if I should make an array of string pointer(to dynamically allocate the memory depending on the size of the array-3 or 5), or just have an array of string with the statically allocated memory of 5. And how to initialize it/set it to null/use it in the constructors.
I'm not allowed to use vector.
When I declared an array of size 5, the problem started in the default constructor.
I don't know how to set it to null...
// string multiple_fruits[5]
// multiple_fruits[] = { nullptr, };
So I'm using an array of string pointer here, is there a better way?
What am I doing wrong?? HAAAALP
//.h file
class Fruit {
char* single_fruit;
string* multiple_fruits;
int num_Fruits;
};
//.cpp file
Fruit::Fruit() {
single_fruit = nullptr;
multiple_fruits = nullptr;
num_Fruits = 0;
}
Fruit::Fruit(const char* singlefruit, string* multiplefruits, int numFruits) {
single_fruit = new char[strlen(singlefruit) + 1];
strcpy_s(single_fruit, strlen(singlefruit) + 1, singlefruit);
multiple_fruits = new string[numFruits];
for (int i = 0; i < numFruits; i++) {
multiple_fruits[i] = multiplefruits[i];
}
num_fruits = numFruits;
}
int main() {
Fruit A;
A("apple", {"banana", "peach", "pear"}, 3)
Fruit B;
B("lemon", {"orange", "pear", "silantro", "ginger", "mandarine"}, 5);
return 0;
}

My approach is to use std::string and std::initializer_list for this:
#include <string>
#include <initializer_list>
class Fruit {
public:
Fruit(const std::string &singlefruit, const std::initializer_list<std::string> &multiplefruits) : single_fruit{singlefruit}, multiple_fruits{new std::string[multiplefruits.size()]}, num_Fruits{multiplefruits.size()} {
for (std::size_t i = 0; i < num_Fruits; ++i) {
multiple_fruits[i] = *(multiplefruits.begin() + i);
}
}
~Fruit() {
delete[] multiple_fruits;
}
private:
std::string single_fruit;
std::string *multiple_fruits;
std::size_t num_Fruits;
};
int main() {
Fruit A("apple", {"banana", "peach", "pear"});
Fruit B("lemon", {"orange", "pear", "silantro", "ginger", "mandarine"});
}
I allocated memory for the elements and deleted the memory in the destructor.
You can't declare a variable in one line and call the constructor in the next line.

Here's one way using a proper declaration of the array argument to the constructor.
#include <algorithm> // std::copy
#include <iterator> // std::begin, std::end
#include <string>
#include <iostream>
template<size_t N>
class Fruit {
public:
Fruit();
Fruit(const std::string& singlefruit, const std::string (&multiplefruits)[N]);
size_t size() const;
private:
std::string single_fruit;
std::string multiple_fruits[N];
};
template<size_t N>
Fruit<N>::Fruit() {}
template<size_t N>
Fruit<N>::Fruit(const std::string& singlefruit, const std::string (&multiplefruits)[N])
: // colon indicates the start of the member initializer list
single_fruit(singlefruit)
{
std::copy(std::begin(multiplefruits), std::end(multiplefruits), multiple_fruits);
}
template<size_t N>
size_t Fruit<N>::size() const { return N; }
int main() {
Fruit A("apple", {"banana", "peach", "pear"});
std::cout << A.size() << '\n';
Fruit B("lemon", {"orange", "pear", "silantro", "ginger", "mandarine"});
std::cout << B.size() << '\n';
}
Output:
3
5

Related

Pointing on vector elements

I've got a vector of objects (apples) and I need a pointer to jump through every element, to print "size" value on every object in "apples". I tried vector::iterator, pointing whole vector but I still cannot get correct solution
#include <iostream>
#include <vector>
class Apple{
public:
int size;
Apple(int size): size(size){}
void print(){
std::cout<<size;
}
};
std::vector<Apple*>apples = {new Apple(21), new Apple(37), new Apple(66)};
Apple* ptr = apples[0];
void nextApple(){
ptr++;
ptr->print(); //returns 0
}
int main()
{
nextApple();
return 0;
}
Big thank You for any help!
To iterate through any T[] array using a pointer, you need to use a T* pointer, and you need to point it initially at the address of the 1st element, not the value of the element.
Your vector's element type is T = Apple*, not T = Apple, so you need to use an Apple** pointer rather than an Apple* pointer, eg:
#include <iostream>
#include <vector>
class Apple{
public:
int size;
Apple(int size) : size(size){}
void print(){
std::cout << size << std::endl;
}
};
std::vector<Apple*> apples = {new Apple(21), new Apple(37), new Apple(66)};
Apple** ptr = &apples[0];
void nextApple(){
++ptr;
(*ptr)->print();
}
int main()
{
nextApple();
return 0;
}
Using an iterator instead (or even an index) would have worked just fine, eg:
#include <iostream>
#include <vector>
class Apple{
public:
int size;
Apple(int size): size(size){}
void print(){
std::cout << size << std::endl;
}
};
std::vector<Apple*> apples = {new Apple(21), new Apple(37), new Apple(66)};
std::vector<Apple*>::iterator iter = apples.begin();
// or: size_t index = 0;
void nextApple(){
++iter;
(*iter)->print();
// or:
// ++index;
// apples[index]->print();
}
int main()
{
nextApple();
return 0;
}
That being said, using a vector of raw Apple* pointers is not a good idea. You have to delete the Apple objects when you are done using them. At the very least, you should wrap the pointers in std::unique_ptr to avoid memory leaks, eg:
#include <iostream>
#include <vector>
#include <memory>
class Apple{
public:
int size;
Apple(int size): size(size){}
void print(){
std::cout << size << std::endl;
}
};
std::vector<std::unique_ptr<Apple>> apples = {std::make_unique<Apple>(21), std::make_unique<Apple>(37), std::make_unique<Apple>(66)};
auto *ptr = &apples[0];
// or: auto iter = apples.begin();
// or: size_t index = 0;
void nextApple(){
++ptr;
(*ptr)->print();
// or:
// ++iter;
// (*iter)->print();
// or:
// ++index;
// apples[index]->print();
}
int main()
{
nextApple();
return 0;
}
But really, just get rid of the dynamic allocation altogther, you don't need it in this example:
#include <iostream>
#include <vector>
class Apple{
public:
int size;
Apple(int size): size(size){}
void print(){
std::cout << size << std::endl;
}
};
std::vector<Apple> apples = {21, 37, 66};
Apple* ptr = &apples[0];
// or: auto iter = apples.begin();
// or: size_t index = 0;
void nextApple(){
++ptr;
ptr->print();
// or:
// ++iter;
// iter->print();
// or:
// ++index;
// apples[index].print();
}
int main()
{
nextApple();
return 0;
}
The code in the question is not idiomatic C++; more like a Java design. In C++ iterators just work:
void show_apples() {
std::vector<Apple> apples =
{ Apple(21), Apple(37), Apple(66) };
for (auto iter = apples.begin(); iter != Apples.end(); ++iter)
std::cout << iter->size << ' ';
std::cout << '\n';
}

How to pass an object data member to a lambda function in C++?

Let's say I have the following non-working code (just for illustration purposes):
class fruit
{
public:
std::string apple;
std::string banana;
std::String orange;
};
class grocery
{
public:
std::vector<fruit> g_items;
std::string total_weight;
};
std::vector<grocery> shopping;
auto check_item = [&](std::string f_itm) -> void
{
for (std::size_t i = 0; i < shopping.g_items.size(); ++i)
{
std::cout << shopping.g_items.at(i).f_itm << std::endl;
}
};
check_item(apple);
check_item(banan);
check_item(orange)
How can I call the lambda function check_item, passing specific data members of the fruit object, since they all have the same type (std:string)?
Thank you.
Try this:
#include <string>
#include <vector>
#include <iostream>
class fruit
{
public:
std::string apple;
std::string banana;
std::string orange; //std::string, not std::String
};
class grocery
{
public:
std::vector<fruit> g_items;
std::string total_weight;
};
std::vector<grocery> shopping;
auto check_item = [&](std::string(fruit::* f_itm)) -> void
{
for (std::size_t i = 0; i < shopping.size(); ++i) //you have two, vectors, so first iterate through shopping
{
for (std::size_t j = 0; j < shopping[i].g_items.size(); ++j) //then, iterate through g_items
std::cout << shopping[i].g_items[i].*f_itm << std::endl; //then, print
}
};
int main() {
check_item(&fruit::apple);
check_item(&fruit::banana); //you put banan, not banana
check_item(&fruit::orange); //remember the ;
}
Your original code had a couple of errors. Then, in your for loop, you have two iterate through both vectors, as the comments explain. Then, it should work.
Change the lambda to take in a pointer-to-member, eg:
class fruit
{
public:
std::string apple;
std::string banana;
std::string orange;
};
class grocery
{
public:
std::vector<fruit> g_items;
std::string total_weight;
};
std::vector<grocery> shopping;
...
auto check_item = [&](std::string (fruit::*f_itm)) -> void
{
for (auto &g : shopping)
{
for (auto &f : g.g_items) {
std::cout << f.*f_itm << std::endl;
}
}
};
check_item(&fruit::apple);
check_item(&fruit::banana);
check_item(&fruit::orange);
Demo

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;
}
};

Can't copy newly created objects in the Class constructor to its vector member in C++

In the class constructor, I am initializing other objects and pushing these objects to my class vector member. From what I understand, the vector create a copy of the object and stores it so that it doesn't go out of scope. However, when verifying the objects in another class function, they are not initialized anymore. Here's a example code to explain the behaviour:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
class Square {
private:
int size_ = 0;
int colour_ = 0;
public:
Square(){
size_ = 0;
colour_ = 0;
}
void init(int size, int colour) {
size_ = size;
colour_ = colour;
}
int get_size() { return size_; }
};
class SetSquares {
private:
std::vector<Square> squares_;
int number_;
public:
SetSquares(): number_(0) {}
void init(int num) {
number_ = num;
squares_.clear();
squares_.resize(num);
for (int i=0; i < num; i++) {
Square square;
square.init(i, i);
squares_.push_back(square);
}
}
void sample(int i) {
if (i >= number_) { return; }
std::cout << "Square size is: " << squares_[i].get_size() << std::endl;
}
};
int main()
{
SetSquares set_of_squares;
set_of_squares.init(7);
set_of_squares.sample(4);
return 0;
}
resize(n) will create n default constructed elements in a vector and push_back will append new elements after those n elements. Use reserve and push_back or resize and index operator as suggested in comment.

passing std::list from one function to another by value

I have a C++ program as given below. I am trying to pass a std::list from one function to another by value. I expect the list to be accessible in the caller function by means of an iterator? I expect that the return will call the copy constructor of std::list and it will be accessible in the caller. Is my assumption wrong ? If not why am I getting a segmentation fault.
#include <list>
#include <map>
#include <string>
#include <set>
#include <iterator>
#include <iostream>
const char *sy_path = "/var/log";
struct Setting
{
typedef std::list<const Setting*> List;
const char* path;
const char* filename;
const char* name;
int def;
int min;
int max;
struct Original
{
const char* filename;
const char* name;
Original(const char* filename_, const char* name_)
:filename(filename_), name(name_)
{
}
}original;
static const List settings();
};
const Setting::List Setting::settings()
{
const Setting c_settings[] =
{ //default min max
{ sy_path, "cs.cfg", "num_a", 1, 1, 29, Original("sys.cfg", "num_a") }
,{ sy_path, "cs.cfg", "num_b", 1, 1, 6, Original("sys.cfg", "num_b") }
,{ sy_path, "cs.cfg", "num_c", 1, 1, 29, Original("sys.cfg", "num_c") }
};
Setting::List lst;
int numelem = sizeof(c_settings) / sizeof(Setting);
for (int i = 0; i < numelem; i++)
{
const Setting & tmpSetting = c_settings[i];
lst.push_back(&tmpSetting);
}
return lst;
}
static int get_settings(void)
{
Setting::List lst;
lst = Setting::settings();
for (Setting::List::const_iterator it = lst.begin() ; it != lst.end(); ++it)
{
const Setting *cs = *it;
std::cout << "path: " <<cs->path << "filename: " <<cs->filename << "name: " << cs->name << std::endl;
}
}
int main()
{
get_settings();
return 0;
}
Yes, return lst; will return a copy of lst. The problem is that you put in lst pointers to data located on the stack (const Setting c_settings[] variable). These pointers become invalid once you return from function, hence the segmentation fault. The solution is to either allocate memory for your settings on heap, or use a std::list<Setting>.
typedef std::list<Setting> List;
lst.push_back(c_settings[i]); // make sure you have the right copy constructor
or
lst.push_back(new Setting(c_settings[i])); // make sure you have the right copy constructor
Also, I would avoid usage of const char * and use std::string instead.