C++ Unable to assign Character Array Variable to String Variable - c++

I am in need of assistance.
I am trying to create a vector structure where the string contents inside of a char array get automatically assign to another STRING variable within the same structure.
After countless hours, I have not been able to figure it out. When I used function such as string(). It doesn't copy anything. I can't seem to assign char variable to string variable. Please advise.
#include <iostream>
#include <ctime>
#include <fstream>
#include <string>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
const int NAME_SIZE = 25;
struct tableInfo
{
char name2[NAME_SIZE];
string name = str(name2); // I need name string to equal name2 character variable
string info;
string link;
};
vector<tableInfo> table(6);
bool compareByWord(const tableInfo &lhs, const tableInfo &rhs);
int main()
{
cin.getline(table[0].name2, 51);
cin.getline(table[1].name2, 51);
cin.getline(table[2].name2, 51);
cin.getline(table[3].name2, 51);
cin.getline(table[4].name2, 51);
sort(table.begin(), table.end(), compareByWord);
cout << table[0].name << endl;
cout << table[1].name << endl;
cout << table[2].name << endl;
cout << table[3].name << endl;
cout << table[4].name << endl;
}
bool compareByWord(const tableInfo &lhs, const tableInfo &rhs)
{
unsigned int length = lhs.name.length();
if (rhs.name.length() < length)
length = rhs.name.length();
int sameLetters = 0;
for (unsigned int i = 0; i < length; ++i)
{
if (sameLetters == length)
return false;
if (tolower(lhs.name[i]) == tolower(rhs.name[i]))
{
++sameLetters;
continue;
}
return(lhs.name[i] < rhs.name[i]);
return(lhs.name[i] < rhs.name[i]);
}
return false;
}

If you want something to happen in a structure (or class), consider making sure it happens by using the constructor. You will need to change your vector so you can no longer default construct the struture, but that's ok.
This works, by enforcing the two fields contain identical data (not withstanding 25 no being long enough):
const int NAME_SIZE = 25;
struct tableInfo
{
tableInfo(char * whatever); //Make it so
char name2[NAME_SIZE];
string name;
string info;
string link;
};
tableInfo::tableInfo(char *whatever)
: name(whatever)
{
strncpy(name2, whatever, NAME_SIZE); //Now they match - unless whatever was too big
}
//vector<tableInfo> table(6);//no longer ok with the non-default constructor
bool compareByWord(const tableInfo &lhs, const tableInfo &rhs);
int main()
{
vector<tableInfo> table;
for (int i = 0; i < 5; ++i)
{
char name2[NAME_SIZE];
cin.getline(name2, 51); //Do you really mean 51?
// It's smaller than NAME_SIZE
table.emplace_back(name2);
}
sort(table.begin(), table.end(), compareByWord);
cout << table[0].name << endl;
cout << table[1].name << endl;
cout << table[2].name << endl;
cout << table[3].name << endl;
cout << table[4].name << endl;
}

How about doing like this:
struct tableInfo
{
char name2[NAME_SIZE];
string name;
string info;
string link;
tableInfo(const char* in_name)
{
memset(name2, 0, NAME_SIZE);
strncpy(name2, in_name, NAME_SIZE - 1);
name = string(name2);
}
};
How to use:
char tmp[NAME_SIZE];
cin.getline(tmp, 51);
table[0] = tableInfo(tmp);

I argue that you don't even need an array in the struct. Just use std::string. getline supports reading into a std::string and you can access individual characters using str[]. If you need to get a pointer to the data you can use str.c_str().
If you really need to use an array in the struct you can create a member function to return a std::string:
struct tableName {
char name[NAME_SIZE];
std::string info;
std::string link;
std::string nameString() const { return name; }
};

Related

accessing an array through pointer

I wanted to create a database like class through OOP. In this class, there are 3 arrays which act as columns in a table.I insert data through insert() function and prints data through printEntries() function. But that function can't access the arrays to retrieve data.
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
using namespace std;
class subject{
public:
int numOfStudents;
string subjectId;
int * marksArray;
int * indexArray;
char * gradeArray;
int index ; // index for inserting data
subject(int students , string subjectName){
numOfStudents = students;
subjectId = subjectName;
this->index =0 ;
//creating and pointing to arrays
int A[numOfStudents]; marksArray = A;
int B[numOfStudents]; indexArray = B;
char C[numOfStudents]; gradeArray = C;
}
void insert(int studentId , int mark , char grade){
indexArray[index] = studentId;
marksArray[index] = mark;
gradeArray[index] = grade;
this->index = this->index +1;
}
int getNumberOfEntries(){
return index ;
}
void printEntries(){
cout<< indexArray[0] << " O" << marksArray[0] << " " << gradeArray[0] << endl;
cout<< indexArray[1] << " OOO" << marksArray[1] << " " << gradeArray[1] << endl;
cout<< indexArray[2] << " OO" << marksArray[2] << " " << gradeArray[2] << endl;
}
};
int main(int argc, char const *argv[]){
subject S(10,"Mathematics");
cout<<S.subjectId<<endl;
cout<<S.numOfStudents<<endl;
S.insert(35,34,'A');
S.insert(33,34,'B');
S.insert(54,34,'C');
S.insert(21,34,'D');
S.insert(14,34,'F');
S.printEntries();
return 0;
}
output is :
Mathematics
10
35 O34 A
0 OOO0
As pointed out by #paddy in the comments to your question, your issue is within the constructor.
//creating and pointing to arrays
int A[numOfStudents]; marksArray = A;
int B[numOfStudents]; indexArray = B;
char C[numOfStudents]; gradeArray = C;
What you are doing is saving the address of the very first element and the rest disappears after you leave the constructor. you are lucky that it's even allowing you to save the first insert and to quote the comment what you are doing is "defined is undefined behavior"
Replacing the pointers with std::vectors and inserting you elements there will be must easier.
class subject
{
public: // It's considered bad practive to have public data members in a class
std::vector<int> marksVector;
std::vector<int> indexVector;
std::vector<char> gradeVector;
// ...
void insert(int studentId , int mark , char grade){
indexVector.push_back(studentId);
marksVector.push_back(mark);
gradeVector.push_back(grade);
// No longer need to worry about index for next insert
}
};

C++ How to properly access dynamic array elements using class method

I've created a class Student which contains a dynamic array. I've filled the first two items with the constructor. Every method I've tried to use to access/print those two elements from the main get a read/access violation and crashes. I've added a cout in the constructor that shows the elements ARE filled and exist. I've included two failed methods in the main: A void function that attempts to send first element to cout, and a method that accepts an int for the desired index. Both have been commented out to allow a test run showing elements are created and printed by the constructor.
Header:
#ifndef STUDENT_H
#define STUDENT_H
#include <string>
#include <iostream>
#define ARRAY_MAX 15
using namespace std;
class Student
{
private:
string firstName, lastName;
unsigned int ID, numItems = 0;
typedef string* StringPtr;
StringPtr items;
public:
int capacity = 15;
Student();
Student(const string fName, const string lName, const unsigned int id);
string getfName() const;
string getlName() const;
void getItem(int num);
string getItemB(int num) const;
unsigned int getID() const;
};
#endif // STUDENT_H
Definitions:
#include "student.h"
using namespace std;
Student::Student()
{
}
Student::Student(const string fName, const string lName, const unsigned int id)
{
firstName = fName;
lastName = lName;
ID = id;
StringPtr items = new string[capacity];
numItems = 0;
items[0] = "stuff";
items[1] = "things";
cout << items[0] << endl << items[1] << endl;
}
string Student::getfName() const
{
return firstName;
}
string Student::getlName() const
{
return lastName;
}
void Student::getItem(int num)
{
cout << items[0] << endl;
}
string Student::getItemB(int num) const
{
return items[num];
}
unsigned int Student::getID() const
{
return ID;
}
Main:
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
#include "student.h"
using namespace std;
int main()
{
Student stu;
string str;
stu = Student("John", "Smith", 1200);
cout << stu.getfName() << " " << stu.getlName() << endl;
//stu.getItem(0);
//cout << stu.getItemB(0);
system("pause");
// Quit without error
return 0;
}
Solution
Change
StringPtr items = new string[capacity];
into
items = new string[capacity];
TL;DR
In
Student::Student(const string fName, const string lName, const unsigned int id)
{
firstName = fName;
lastName = lName;
ID = id;
StringPtr items = new string[capacity];
numItems = 0;
items[0] = "stuff";
items[1] = "things";
cout << items[0] << endl << items[1] << endl;
}
The line
StringPtr items = new string[capacity];
creates a new Automatic (local) variable items and initializes it rather than the intended private member variable StringPtr items;. This is commonly known as Variable Shadowing. The local items goes out of scope at the end of the function leaking the allocated memory and the member items is never initialized leading to stu.getItem(0); accessing an uninitialized pointer and triggering the crash.
void Student::getItem(int num)
{
cout << items[0] << endl; // items points Crom knows where, so items[0] is invalid.
}
This crash is quite fortunate. Accessing an unitintialized pointer results in Undefined Behaviour which means it could do anything from looking like it works to causing the universe to explode.
The next problem you have to deal with is observing The Rule of Three.
The best way to deal with this is std::vector
std::vector<string> items;
std::vector is Rule of Three (Rule of Five, actually) compliant so that you don't have to be.
If std::vector is not allowed, you need to implement a copy constructor
Student::Student(const Student & src):
firstName(src.firstName), lastName(src.lastName),
ID(src.ID), numItems(src.numItems),
items(new string[capacity])
{
for (int i = 0; i < src.numItems; i++)
{
items[i] = src.items[i];
}
}
and an assignment operator (Taking advantage of the Copy and Swap Idiom for simplicity)
Student & Student::operator=(Student src)
{
swap(*this,src);
return *this;
}
Writing swap I'll leave up to you.

Passing array of strings to a function

I am trying to create a program that uses class, arrays, and functions to show information about two students(Name, id#, classes registered). The part I am struggling with is passing arrays to a function. How do I do that?
#include <string>
#include <iostream>
#include <iomanip>
using namespace std;
class Student // Student class declaration.
{
private:
string name;
int id;
string classes;
int arraySize;
public:
void setName(string n)
{
name = n;
}
void setId(int i)
{
id = i;
}
void setClasses(string c, int num)
{
classes = c;
arraySize = num;
}
string getName()
{
return name;
}
int getId()
{
return id;
}
void getClasses()
{
for (int counter=0; counter <arraySize; counter++) {
cout << classes[counter] << endl;
}
}
};
int main()
{
//Student 1
string s1Name = "John Doe";
int s1Id = 51090210;
int const NUMCLASSES1 = 3;
string s1Classes[NUMCLASSES1] = {"C++","Intro to Theatre","Stagecraft"};
//Student 2
string s2Name = "Rick Harambe Sanchez";
int s2Id = 666123420;
int const NUMCLASSES2 = 2;
string s2Classes[NUMCLASSES2] = {"Intro to Rocket Science","Intermediate Acting"};
//
Student info;
info.setName(s1Name);
info.setId(s1Id);
//info.setClasses(s1Classes, NUMCLASSES1);
cout << "Here is Student #1's information:\n";
cout << "Name: " << info.getName() << endl;
cout << "ID: " << info.getId() << endl;
//cout << "Classes: " << info.getClasses() << endl;
info.setName(s2Name);
info.setId(s2Id);
// info.setClasses(s2Classes, NUMCLASSES1);
cout << "\n\nHere is student #2's information:\n";
cout << "Name: " << info.getName() << endl;
cout << "ID: " << info.getId() << endl;
//cout << "Classes: " << info.getClasses() << endl;
return 0;
}
The usual way to pass around variable-length lists in C++ is to use an std::vector. A vector is a single object that you can easily pass to a function, copying (or referencing) its contents. If you are familiar with Java, it's basically an ArrayList. Here is an example:
#include <vector>
#include <string>
using namespace std;
class foo {
private:
vector<string> myStrings;
public:
void setMyStrings(vector<string> vec) {
myStrings = vec;
}
}
//...
foo myObj;
vector<string> list = {"foo","bar","baz"};
myObj.setMyStrings(list);
If don't want to use the standard library though, you can pass an array C-style. This involves passing a pointer to the first element of the array, and the length of the array. Example:
void processStrings(string* arr, int len) {
for (int i = 0; i < len; i++) {
string str = arr[i];
//...
}
}
string array[] = {"foo","bar","baz"};
processStrings(array, 3); // you could also replace 3 with sizeof(array)
Passing raw arrays like this, especially if you wanted to then copy the array into an object, can be painful. Raw arrays in C & C++ are just pointers to the first element of the list. Unlike in languages like Java and JavaScript, they don't keep track of their length, and you can't just assign one array to another. An std::vector encapsulates the concept of a "list of things" and is generally more intuitive to use for that purpose.
Life lesson: use std::vector.
EDIT: See #nathanesau's answer for an example of using constructors to initialize objects more cleanly. (But don't copy-paste, write it up yourself! You'll learn a lot faster that way.)
You can pass array of any_data_type to function like this
void foo(data_type arr[]);
foo(arr); // If you just want to use the value of array
foo(&arr); // If you want to alter the value of array.
Use std::vector. Also, don't add functions you don't need. Here's an example of using std::vector
#include <string>
#include <iostream>
#include <vector>
using std::string;
using std::vector;
class Student // Student class declaration.
{
private:
vector<string> classes;
string name;
int id;
public:
Student (const vector<string> &classesUse, string nameUse, int idUse) :
classes (classesUse),
name (nameUse),
id (idUse)
{
}
void print ()
{
std::cout << "Name: " << name << std::endl;
std::cout << "Id: " << id << std::endl;
std::cout << "Classes: ";
for (int i = 0; i < classes.size (); i++)
{
if (i < classes.size () - 1)
{
std::cout << classes[i] << ", ";
}
else
{
std::cout << classes[i] << std::endl;
}
}
std::cout << std::endl;
}
};
int main()
{
Student John ({"C++","Intro to Theatre","Stagecraft"},
"John",
51090210);
John.print ();
Student Rick ({"Intro to Rocket Science","Intermediate Acting"},
"Rick",
666123420);
Rick.print ();
return 0;
}
Name: John
Id: 51090210
Classes: C++, Intro to Theatre, Stagecraft
Name: Rick
Id: 666123420
Classes: Intro to Rocket Science, Intermediate Acting
In the private variables of class Student, you are storing a string:
String classes;
where as you should be storing an array of strings like:
String classes[MAX_NUM_CLASSES];
then in the set classes function, pass in an array of strings as the first argument, so it should be :
void setClasses(string[] c, int num)
{
classes = c; //not sure if simply setting them equal will work, rather copy entire array using a for loop
arraySize = num;
}
This should point you in the right direction
Also, use std::vector instead of string[], it will be easier.

How do you initialize template class members that uses other template classes?

I'm having trouble correctly setting up and accessing my member functions of a class. This node class is being used to build a Max Heap Tree. However, when the tree is being initialized, I'm getting garbage data and not what I am initializing it to.
#ifndef HEAPNODE_H_INCLUDED
#define HEAPNODE_H_INCLUDED
#include <iostream>
#include <cstdlib>
#include <array>
using namespace std;
template <class Type> class HeapNode {
private:
int key;
Type value;
public:
HeapNode(int key, Type const &value) {
this->key = key;
this->value = value;
}
// Returns the key of the node
int getKey() {
return key;
}
// Returns the value of the node
Type getValue() {
return value;
}
// Displays the node
void displayNode() {
cout << "Key: " << key << "\tValue: " << value << endl;
}
};
#endif
Here is the class that builds my Heap Tree. I've tried setting the initializations in the constructor every which way, and I'm still getting junk data. In addition, I set the constructor to take an integer, but when I'm creating a tree in my driver program, it won't let me put an argument for it which initiates an array of that size.
#ifndef MAXHEAPTREE_H_tINCLUDED
#define MAXHEAPTREE_H_INCLUDED
#include <iostream>
#include <cstdlib>
#include <fstream>
#include <string>
#include "HeapNode.h"
using namespace std;
template <class Type> class MaxHeapTree {
private:
HeapNode<Type> *array;
HeapNode<Type> *root;
int elementSize;
int height;
int leafCounter;
public:
// Constructor
MaxHeapTree(int n = 10) : elementSize(0), height(0), leafCounter(0) {
this->elementSize = elementSize;
this->height = height;
this->leafCounter = leafCounter;
HeapNode<Type> *array = new HeapNode<Type>[n];
}
// Destructor
~MaxHeapTree();
void arrayDisplay() {
cout << "Original array size: " << sizeof(array)/4 << endl;
}
// Returns the number of elements in the tree
int getSize() {
return elementSize;
}
// Returns the height of the tree
int getHeight() {
return height;
}
// Returns the number of leaves in the tree
int leaves() {
return leafCounter;
}
int countLines(const string fileName) {
string line;
int lineCount = 0;
ifstream myFile (fileName.c_str());
if (myFile.is_open()) {
while (getline(myFile, line)) {
lineCount++;
}
}
else {
cout << "Error opening file" << endl;
}
myFile.close();
return lineCount;
}
// Reads structure from a text file and builds a max heap
void buildTree(const string fileName) {
string line;
string key;
string value;
int lines = countLines(fileName);
int i = 0;
cout << "Lines: " << lines << endl;
HeapNode<Type> *newArray[lines];
cout << "Size of newArray: " << sizeof(newArray)/4 << endl;
ifstream myFile (fileName.c_str());
if (myFile.is_open()) {
while (getline(myFile, line)) {
key = line.substr(0, 1);
int x = atoi(key.c_str());
value = line.substr(1);
HeapNode<Type> *hNode = new HeapNode<Type>(x, value);
newArray[i] = hNode;
cout << "newArray[" << i << "] = ";
newArray[i]->displayNode();
i++;
}
}
else {
cout << "2 - Error opening file." << endl;
}
myFile.close();
}
};
#endif
How do you initialize template class members that uses other template classes?
In the same way you initialize members of non templates that don't use other templates.
when the tree is being initialized, I'm getting garbage data and not what I am initializing it to.
I was using MaxHeap<string> *heapTree1;
Well, there's your problem. Apparently you never created an instance of MaxHeap<string>.

How to solve the error: ISO C++ forbids variable-size array

I keep getting an error that says ISO C++ forbids variable-size array.
I am suppose to have an output that displays
Level Score Stars
----------------------------------
1 3840 ***
and so on....
Here is my program
#include <iostream> // access to cin, cout
#include <cstring>
#include <cstdlib>
#include<fstream>
using namespace std;
int buildArrays(int A [], int B [], int C [])
{
int i = 0, num;
ifstream inFile;
inFile.open("candycrush.txt");
if (inFile.fail())
{
cout << "The candycrush.txt input file did not open" << endl;
exit(-1);
}
while (inFile)
{
inFile >> num;
A[i] = num;
inFile >> num;
B[i] = num;
inFile >> num;
C[i] = num;
i++;
}
inFile.close();
return i;
}
void printArrays(string reportTitle, int levelsArray [], int scoresArray [], int starsArray [], int numberOfLevels)
{
cout << endl;
cout << reportTitle << endl;
cout << "Levels\tScores\tStars" << endl;
cout << "---------------------" << endl;
for (int i = 0; i < numberOfLevels; i++)
{
cout << levelsArray[i] << "\t" << scoresArray[i] << "\t";
for (int j = 0; j < starsArray[i]; j++)
{
cout << "*";
}
cout << endl;
}
}
void sortArrays(int levelsArray [], int scoresArray [], int starsArray [], int numberOfLevels)
{
for (int i = 0; i < numberOfLevels; i++)
{
for (int j = 0; j < numberOfLevels; j++)
{
if (levelsArray[i] < levelsArray[j])
{
int temp1 = levelsArray[i];
int temp2 = scoresArray[i];
int temp3 = starsArray[i];
levelsArray[i] = levelsArray[j];
scoresArray[i] = scoresArray[j];
starsArray[i] = starsArray[j];
levelsArray[j] = temp1;
scoresArray[j] = temp2;
starsArray[j] = temp3;
}
}
}
}
int main()
{
int MAX = 400; (This is where I am getting my valid array size error)
int levelsArray[MAX];
int scoresArray[MAX];
int starsArray[MAX];
int numberOfLevels = buildArrays(levelsArray, scoresArray, starsArray);
printArrays("Candy Crush UNSORTED Report", levelsArray, scoresArray, starsArray, numberOfLevels);
sortArrays(levelsArray, scoresArray, starsArray, numberOfLevels);
printArrays("Candy Crush SORTED Report", levelsArray, scoresArray, starsArray, numberOfLevels);
system("pause");
}
Unless you (or your teacher, if you're doing this as homework) are intent on doing this badly, you should not simply convert MAX to a const.
Instead you should use std::vector instead of using arrays at all.
As long as you're at it:
Create a struct to hold the three parts of a single score.
Use std::sort instead of your own sorting function.
Overload operator>> and operator<< to do I/O on score objects.
Prefer one-step initialization over default-construction followed by the real initialization (e.g., creating, then separately opening a stream as well as creating then separately filling the vector).
Don't ever use while (stream) read_data1. Always test the result of reading the data, and react to that result.
Using those, we end up with code something like this:
struct score {
int level;
int score;
int stars;
bool operator<(score const &other) const {
return level < other.level;
}
friend std::istream &operator>>(std::istream &is, score &s) {
return is >> s.level >> s.score >> s.stars;
}
friend std::ostream &operator<<(std::ostream &os, score const &s) {
return os << s.level << "\t"
<< s.score << "\t"
<< std::string(s.stars, '*');
}
};
int main() {
std::ifstream in("candycrush.txt");
std::vector<score> scores{std::istream_iterator<score>(in),
std::istream_iterator<score>()};
std::cout << "Unsorted:\n";
for (auto const &s : scores)
std::cout << s << "\n";
std::cout << "\n";
std::sort(scores.begin(), scores.end());
std::cout << "Sorted:\n";
for (auto const &s : scores)
std::cout << s << "\n";
}
You should probably also add something to deal with two scores with equal levels (e.g., by comparing scores in that case), but that's probably a subject for another answer/diatribe.
1. ...or while (stream.good()) or while (!stream.eof()).
gcc support variable size arrays, but other compilers do not. Try.
int main()
{
#define MAX 400 /* use macro instead of stack variable */
int levelsArray[MAX];
int scoresArray[MAX];
int starsArray[MAX];
/* rest of your code */
return 0;
}
The array size should be a compile time constant in C++ for most compilers. Try
#define MAX 400;
...
int levelsArray[MAX];
or
const int MAX=400;
...
int levelArray[MAX];
You need to make the MAX variable to const variable.
Try this:
const int MAX=400;
#include <iostream> // access to cin, cout
#include <cstring>
#include <cstdlib>
#include<fstream>
#define MAX 400 //<- Try this
using namespace std;
I would also recommend using classes when dealing with multiple arrays.
With the use of a class, there will be no need for you to pass multiple arrays to a function and set the parameter of the function with that long list of arrays. Such as this one:
void printArrays(string reportTitle, int levelsArray [], int scoresArray [], int starsArray [], int numberOfLevels)
Follow the vector usage instead (when variable sized array required for your purposes): https://stackoverflow.com/a/49021923/4361073