I'm attempting to convert a working static String class to a dynamic String class using pointers, but when I implement the pointer it throws a segmentation fault error. Code below:
mystring1.h:
//File: mystring1.h
// Declaration file for user-defined String class.
#ifndef _MYSTRING_H
#define _MYSTRING_H
#include<iostream>
#include <cstring>
using namespace std;
#define MAX_STR_LENGTH 200
class String {
public:
String();
String(const char *s); // a conversion constructor
void append(const String &str);
//deconstructor
~String();
//copy constructor
String (const String & origString);
//assignment operator overload
String operator =(const String &origString);
// Relational operators
bool operator >(const String &str) const;
bool operator <(const String &str) const;
String operator +=(const String &str);
void print(ostream &out) const;
int length() const;
char operator [](int i) const; // subscript operator
private:
char *contents;
int len;
//int capacity;
};
ostream & operator<<(ostream &out, const String & r); // overload ostream operator "<<" - External!
#endif /* not defined _MYSTRING_H */
mystring1.cpp:
//File: mystring1.h
// Implementation file for user-defined String class.
#include "mystring1.h"
String::String()
{
contents[0] = '\0';
len = 0;
}
String::String(const char s[])
{
len = strlen(s);
contents = new char[len + 1];
strcpy(contents, s);
}
void String::append(const String &str)
{
strcat(contents, str.contents);
len += str.len;
}
//deconstructor
String::~String()
{
delete [] contents;
}
//copy constructor
String::String(const String &origString)
{
len = origString.len;
contents = new char[len + 1];
for (int i = 0; i < len; i++)
{
contents[i] = origString.contents[i];
}
}
//assignment operator overload
String String::operator =(const String &origString)
{
if (this != &origString)
{
len = origString.len;
delete [] contents;
contents = new char[len + 1];
for (int i = 0; i < len; i++)
{
contents[i] = origString.contents[i];
}
}
return *this;
}
bool String::operator >(const String &str) const
{
return strcmp(contents, str.contents) > 0;
}
bool String::operator <(const String &str) const
{
return strcmp(contents, str.contents) < 0;
}
String String::operator +=(const String &str)
{
append(str);
return *this;
}
void String::print(ostream &out) const
{
out << contents;
}
int String::length() const
{
return len;
}
char String::operator [](int i) const
{
if (i < 0 || i >= len) {
cerr << "can't access location " << i
<< " of string \"" << contents << "\"" << endl;
return '\0';
}
return contents[i];
}
ostream & operator<<(ostream &out, const String & s) // overload ostream operator "<<" - External!
{
s.print(out);
return out;
}
Assignment 5 Driver:
/**
* cmpsc122 Assignment 5 test file
* File Name: Assign5driver.cpp
*
* Description: This program demonstrates a basic String class that implements
* dynamic allocation and operator overloading.
*
*/
#include <iostream>
#include "mystring1.h"
using namespace std;
/*************************** Main Program **************************/
int main()
{
String str1, str2("dog"); // Using constructor for initial strings
char s1[100], s2[100]; // Some C strings.
// Print out initial strings
cout << "Initial values:" << endl;
cout << "str1 holds \"" << str1 << "\" (length = " << str1.length() << ")" << endl;
cout << "str2 holds \"" << str2 << "\" (length = " << str2.length() << ")" << endl;
// Inputs some new strings in them
cout << "\nEnter a value for str1 (no spaces): ";
cin >> s1;
str1 = s1;
cout << "\nEnter a value for str2 (no spaces): ";
cin >> s2;
str2 = s2;
cout << "\nAfter assignments..." << endl;
cout << "str1 holds \"" << str1 << "\" (length = " << str1.length() << ")" << endl;
cout << "str2 holds \"" << str2 << "\" (length = " << str2.length() << ")" << endl;
// Get some elements...
int i;
cout << "\nEnter which element of str1 to display: ";
cin >> i;
cout << "Element #" << i << " of str1 is '" << str1[i] << "'" << endl;
cout << "\nEnter which element of str2 to display: ";
cin >> i;
cout << "Element #" << i << " of str2 is '" << str2[i] << "'" << endl;
// Concate some strings
cout << "\nEnter a value to append to str1 (no spaces): ";
cin >> s1;
// str1.append(s1); // Actually, the cstring is converted to String object here by the constructor
str1 += s1; // same as above
cout << "\nEnter a value to append to str2 (no spaces): ";
cin >> s2;
str2 += s2;
cout << "\nAfter appending..." << endl;
cout << "str1 holds \"" << str1 << "\" (length = " << str1.length() << ")" << endl;
cout << "str2 holds \"" << str2 << "\" (length = " << str2.length() << ")" << endl;
// Compare strings...
cout << "\nComparing str1 and str2..." << endl;
cout << "\"";
cout<< str1; // test the overloading of ostream operator <<
cout << "\" is ";
if (str1 < str2) { // test the overloading of comparison operator <
cout << "less than";
} else if (str1 > str2) {
cout << "greater than";
} else {
cout << "equal to";
}
cout << " \"";
cout << str2;
cout << "\"" << endl;
cout << "\ntest the = operator, after str1 = str2; "<< endl;
str1 = str2;
cout << "str1 holds \"" << str1 << "\" (length = " << str1.length() << ")" << endl;
cout << "str2 holds \"" << str2 << "\" (length = " << str2.length() << ")" << endl;
str1 += s1;
cout << "\nAfter str1 = str1 + s1: "<< endl;
cout << "str1 holds \"" << str1 << "\" (length = " << str1.length() << ")" << endl;
cout << "str2 holds \"" << str2 << "\" (length = " << str2.length() << ")" << endl;
String str3(str2);
cout << "\ntest the copy constructor, after str4(str3);"<< endl;
cout << "str2 holds \"" << str2 << "\" (length = " << str2.length() << ")" << endl;
cout << "str3 holds \"" << str3 << "\" (length = " << str3.length() << ")" << endl;
cout << "\nafter appending str2 by str1" << endl;
str2 += str1;
cout << "str2 holds \"" << str2 << "\" (length = " << str2.length() << ")" << endl;
cout << "str3 holds \"" << str3 << "\" (length = " << str3.length() << ")" << endl;
cout<< "\nstr3 are not changed. Type any letter to quit." << endl;
char q;
cin >> q;
return 0;
}
Any help would be appreciated, I have tried all that I can and my research into segmentation fault causes isn't doing much to help.
There are several immediately obvious bugs in your implementation:
The default constructor uses uninitialized contents pointer.
The ::append() method does not ensure there is sufficient space in the destination buffer before performing strcat.
The copy constructor and the ::operator=() method do not NUL-terminate the contents buffer.
There are likely more.
You should build your test program with g++ -fsanitize=address ... and fix all the bugs it finds.
You should also build your program with -Wall -Wextra flags, and fix all the warning it produces.
Related
I have a button that appends from lineEdit to a *.txt file.
I want to know if there is something i can do to get something like this :
Student N°1:
FirstName : -----
LastName: -----
Age: -----
Student N°2:
FirstName : -----
LastName: -----
Age: -----
I want that everytime my program checks last student number (the one inserted before) and adds +1 to the one i'm trying to append.
QFilefile("***");
if (file.open(QFile::Append)) {
QTextStream out(&file);
out <<"Student Num:"<<"\n";
out <<"Name:" << ui->lineEdit->text()<<"\n";
The File I have Now :
FirstName :
LastName :
Age :
FirstName :
LastName :
Age :
Desired Output :
Student N°1
FirstName :
LastName :
Age :
Student N°2
FirstName :
LastName :
Age :
I want that N°(var) to be +1 everytime i hit save button
this might help you along.
now you only need to make a method / function to update a specific "student" datatype and write back to file.
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
#include <iostream>
#include <ctime>
using namespace std;
typedef std::vector<string> Vector;
struct Student
{
Student () : firstName(""), lastName(""), age(0){};
Student (const std::string& firstname, const std::string& lastname, unsigned short age) : firstName(firstname), lastName(lastname), age(age){};
std::string firstName;
std::string lastName;
unsigned short age;
};
typedef std::vector<Student> studentVector;
void readReadFile(string &fileName, studentVector &array){
std::ifstream file(fileName);
if(file.fail()){
//File does not exist code here
std::cout << "File doesn't exist." << endl;
return;
}
else{
int counter = 0;
std::string str;
string studentName = "";
string studentLastName = "";
int age = 0;
while (std::getline(file, str)) {
if(counter == 0){
//++counter;
std::string strName="FirstName :";
std::string str2 = strName.substr (0,11);
//std::cout << "FirstName : " << str2 << endl;
if(str.length() < 11){
return; // no name present
}
std::string::size_type posName = str.find(str2);
//std::cout << "size_t: " << posName << endl;
if (posName != string::npos) {
//.. found.
std::string str3 = str.substr (12, str.length());
std::cout << "Name: " << str3 << endl;
//std::cout << "size_t: " << posName << endl;
std::cout << "found Name" << endl;
studentName = str3;
}
}
if(counter == 1){
//++counter;
std::string strName="LastName :";
std::string str2 = strName.substr (0,10);
//std::cout << "LastName: " << str2 << "length: " << str2.length() << endl;
if(str.length() < 10){
return; // no lastname present
}
std::string::size_type posLastName = str.find(str2);
//std::cout << "size_t: " << posName << endl;
if (posLastName != string::npos) {
//.. found.
std::string str3 = str.substr (11, str.length());
std::cout << "LastName: " << str3 << endl;
//std::cout << "posLastName : " << posLastName << endl;
std::cout << "found lastName" << endl;
studentLastName = str3;
}
}
if(counter == 2){
//++counter;
std::string strName="Age :";
std::string str2 = strName.substr (0,5);
//std::cout << "Age: " << str2 << endl;
if(str.length() < 5){
return; // no age present
}
std::string::size_type posAge = str.find(str2);
//std::cout << "size_t: " << posName << endl;
if (posAge != string::npos) {
//.. found.
std::string str3 = str.substr (6, str.length());
std::cout << "Age: " << str3 << endl;
//std::cout << "posAge : " << posAge << endl;
std::cout << "found age" << endl;
age = std::stoi(str3);
}
}
++counter;
std::string::size_type posName = str.find("---");
if (posName != string::npos) {
counter = 0;
Student test(studentName, studentLastName, age);
array.push_back(test);
std::cout << "------------------" << endl;
// std::cout << "empty" << std::endl; // white line
}
}
file.close();
}
}
void appendstuff(Student data, studentVector &array){
array.push_back(data);
}
void write_students_backtofile(string &fileName, studentVector &array){
// https://stackoverflow.com/questions/17032970/clear-data-inside-text-file-in-c
std::ofstream myfile;
myfile.open(fileName, std::ofstream::out | std::ofstream::trunc);
//myfile.close();
// write vector back to file
//ofstream myfile;
//myfile.open (fileName);
for(auto& i : array){
myfile << "FirstName : " << i.lastName << "\n";
myfile << "LastName : " << i.firstName << "\n";
myfile << "Age : " << i.age << "\n";
//myfile << "\n"; // white line
myfile << "---\n"; // white line
}
myfile.close();
array.clear();
}
int main(){
std::clock_t start;
double duration;
start = std::clock();
studentVector array;
string fileName = "input.txt";
readReadFile(fileName, array);
Student test2("Frodo", "Baggins", 66);
Student test3("Gandalf", "the Grey", 254);
Student test4("Saruman", "the White", 450);
appendstuff(test2, array);
appendstuff(test3, array);
appendstuff(test4, array);
std::cout << "----------------------------------" << endl;
for(auto& i : array){
std::cout << i.lastName << " " << i.firstName << " " << i.age << endl;
}
string testFile = "test.txt"; // for testing.
write_students_backtofile(fileName, array);
duration = ( std::clock() - start ) / (double) CLOCKS_PER_SEC;
std::cout<<"printf: "<< duration << " seconds" << '\n';
return 0;
}
I'm currently writing a program that uses string functions. I need some advice/hints on how I can display "Hello World" and its length with myStrcat() in main(). I'm new to programming and any support would be greatly appreciated.
My Code:
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
int myStrlen(char str1[])
{
int i = 0;
for (i=0; str1[i] != '\0'; i++)
str1[i] = '\0';
return i;
}
int myStrcat(char str2[], char str3[])
{
}
int myStrcpy(char str4[], char str5[])
{
int i = 0;
for (i=0; str5[i] != '\0'; i++)
str4[i] = str5[i];
str4[i] = '\0';
return i;
}
int main()
{
const int SIZE = 11;
char s1[SIZE] = "Hello";
char s2[SIZE] = "World";
cout << "s1: " << " " << s1 << endl << endl; ///Should display "Hello"
cout << "The length of s1: " << myStrlen(s1) << endl << endl;
cout << "Doing strcat(s1, s2) " << endl;
myStrcat(s1, s2);
cout << "s1: " << " " << s1 << endl; /// Should display "Hello World"
cout << "The length of s1: " << myStrlen(s1) << endl << endl;
cout << "Doing strcpy(s1, s2) " << endl;
myStrcpy(s1, s2);
cout << "s1: " << " " << s1 << endl; /// Should display "World"
cout << "The length of s1: " << myStrlen(s1) << endl << endl;
My Output:
s1: Hello
The length of s1: 5
Doing strcat(s1, s2)
s1:
The length of s1: 0
Doing strcpy(s1, s2)
s1: World
The length of s1: 5
Line 6 and 7 are suppose to display Hello World and its length (which is 11).
You have a number of not just quite right beginning to each of your functions. Firstly, let's think about the returns for each. myStrlen should return size_t instead of int. C++ designates a size_type for counters, measuring, etc.. The remaining functions should return char* (or nullptr on failure).
Looking at your myStrlen function where you have
for (i=0; str1[i] != '\0'; i++)
str1[i] = '\0';
You are setting every character in str1 to the nul-character because you are applying the loop to the next expression. You should not be worrying about nul-terminating anything within myStrlen -- you are just counting characters. So you can rewrite it as follows:
size_t myStrlen (const char *str)
{
size_t l = 0;
for (; str[l]; l++) {}
return l;
}
Your myStrcpy looks workable, though you should always validate your input parameters are not nullptr before using them -- I leave that to you. Since you have a myStrlen function, you can simply use that along with memcpy to create your myStrcpy function as:
char *myStrcpy (char *dest, const char *src)
{
size_t len = myStrlen(src);
return (char *)memcpy (dest, src, len + 1);
}
(note: traditionally you have source (src) and destination (dest) parameters when copying or concatenating)
For your myStrcat function, you are just using the myStrlen function to find the offset in dest to append src, so you really just need a call to myStrlen and then a call to myStrcpy to copy src to that offset in dest, e.g.
char *myStrcat (char *dest, const char *src)
{
size_t len = myStrlen (dest);
return myStrcpy (dest + len, src);
}
In your main(), if you want a space between "Hello" and "World", then const int SIZE = 11; is one too-low to hold the concatenated string "Hello World" which would require 12-bytes (including the nul-terminating character). Do Not Skimp on buffer size. 128 is plenty small.
Remaining with your main() but updating SIZE = 12; and adding a space between "Hello" and "World" with an additional call to myStrcat, you could do the following:
int main (void)
{
const int SIZE = 12; /* too short by 1 if you add space between */
char s1[SIZE] = "Hello";
char s2[SIZE] = "World";
std::cout << "s1: " << " " << s1 << std::endl << std::endl;
std::cout << "The length of s1: " << myStrlen(s1) << std::endl << std::endl;
std::cout << "Doing strcat(s1, s2) " << std::endl;
myStrcat(s1, " ");
myStrcat(s1, s2);
std::cout << "s1: " << " " << s1 << std::endl;
std::cout << "The length of s1: " << myStrlen(s1) << std::endl << std::endl;
std::cout << "Doing strcpy(s1, s2) " << std::endl;
myStrcpy(s1, s2);
std::cout << "s1: " << " " << s1 << std::endl;
std::cout << "The length of s1: " << myStrlen(s1) << std::endl << std::endl;
}
(note: don't include using namespace std;, it is just bad form in this day and age)
Example Use/Output
$./bin/mystrcpy
s1: Hello
The length of s1: 5
Doing strcat(s1, s2)
s1: Hello World
The length of s1: 11
Doing strcpy(s1, s2)
s1: World
The length of s1: 5
Look things over and let me know if you have further questions.
First you should read Why is “using namespace std;” considered bad practice?
Don't use c style strings if you are starting programming. Use std::string. It's much simpler to use.
#include <iostream>
#include <string>
int myStrlen(const std::string &str) {
return str.length();
}
int myStrcat(std::string &str1, const std::string &str2) {
str1 += str2;
str1.length();
}
int myStrcpy(std::string &str1, const std::string &str2) {
str1 = str2;
return str1.length();
}
int main() {
std::string s1 = "Hello";
std::string s2 = "World";
std::cout << "s1: " << s1 << "\n\n"; ///Should display "Hello"
std::cout << "The length of s1: " << myStrlen(s1) << "\n\n";
std::cout << "Doing strcat(s1, s2) " << '\n';
myStrcat(s1, s2);
std::cout << "s1: " << s1 << '\n'; /// Should display "Hello World"
std::cout << "The length of s1: " << myStrlen(s1) << "\n\n";
std::cout << "Doing strcpy(s1, s2) " << '\n';
myStrcpy(s1, s2);
std::cout << "s1: " << s1 << '\n'; /// Should display "World"
std::cout << "The length of s1: " << myStrlen(s1) << "\n\n";
return 0;
}
In my assignment I was asked to create the Product class, and I have finished all the implementations except the "non-member IO operator". The question I found it very vague, it asks me to overload the << and >> operators to work with ostream and istream to read a Product from and print a Product to the console in order to make this main function work.
Here I see the main function has cout or cin to Product's derived class SItem, I wonder how I should implement the << >> operators to make the main work.
My main:
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include "Product.h"
#include <fstream>
#ifdef TAB
# undef TAB
#endif
#define TAB '\t'
using namespace std;
namespace sict {
class SItem :public Product {
public:
SItem(const char* theSku, const char * theName) :Product(theSku, theName) {}
SItem() {}
virtual std::fstream& store(std::fstream& file, bool addNewLine = true)const {
if (!isEmpty()) {
file.open("ms4.txt", ios::out | ios::app);
file << sku() << TAB << name() << TAB << quantity() << TAB << qtyNeeded() << TAB
<< int(taxed()) << TAB << price() << endl;
file.clear();
file.close();
}
return file;
}
virtual std::fstream& load(std::fstream& file) {
file.open("ms4.txt", ios::in);
char buf[2000];
double dbuf;
int ibuf;
file >> buf;
sku(buf);
file >> buf;
name(buf);
file >> ibuf;
quantity(ibuf);
file >> ibuf;
qtyNeeded(ibuf);
file >> ibuf;
taxed(ibuf != 0);
file >> dbuf;
price(dbuf);
file.clear();
file.close();
return file;
}
virtual std::ostream& write(std::ostream& os, bool linear)const {
return isEmpty() ? os : (os << sku() << ": " << name() << ", qty: "
<< quantity() << ", qtyNeeded:" << qtyNeeded()
<< ", Cost: " << fixed << setprecision(2) << cost());
}
virtual std::istream& read(std::istream& is) {
char buf[2000];
double dbuf;
int ibuf;
cout << "Sku: ";
is >> buf;
sku(buf);
cout << "Name (no spaces): ";
is >> buf;
name(buf);
cout << "Qty: ";
is >> ibuf;
quantity(ibuf);
cout << "Qty Needed: ";
is >> ibuf;
qtyNeeded(ibuf);
cout << "Is taxed? (1/0): ";
is >> ibuf;
taxed(ibuf != 0);
cout << "Price: ";
is >> dbuf;
price(dbuf);
return is;
}
};
}
void dumpFile(fstream& f) {
f.open("ms4.txt", ios::in);
char ch;
while (!f.get(ch).fail()) {
cout.put(ch);
}
f.clear();
f.close();
}
using namespace sict;
void test() {
double res, val = 0.0;
fstream F("ms4.txt", ios::out);
F.close();
SItem S;
SItem T;
SItem U;
cout << "Enter Product info: " << endl;
cin >> S;
SItem V = S;
S.store(F);
T.load(F);
cout << "T: (store, load)" << endl;
cout << T << endl;
cout << "S: " << endl;
cout << S << endl;
cout << "V(S): " << endl;
cout << V << endl;
cout << "U=T & op= :" << endl;
U = T;
cout << U << endl;
cout << "Operator == :" << endl;
cout << "op== is " << (T == "1234" ? "OK" : "NOT OK") << endl;
cout << "op+=: " << endl;
U += 10;
cout << U << endl;
cout << "op+=double : " << endl;
res = val += U;
cout << res << "=" << val << endl;
}
int main() {
fstream F("ms4.txt", ios::out);
F.close();
SItem S;
SItem U("4321", "Rice");
cout << "Empty Prouduct:" << endl << S << endl;
cout << "U(\"4321\", \"Rice\"):" << endl << U << endl;
cout << "Please enter the following information:" << endl;
cout << "Sku: 1234" << endl;
cout << "Name(no spaces) : Blanket" << endl;
cout << "Qty : 12" << endl;
cout << "Qty Needed : 23" << endl;
cout << "Is taxed ? (1 / 0) : 1" << endl;
cout << "Price : 12.34" << endl;
test();
cout << "Please enter the following information:" << endl;
cout << "Sku: 1234" << endl;
cout << "Name(no spaces) : Jacket" << endl;
cout << "Qty : 12" << endl;
cout << "Qty Needed : 23" << endl;
cout << "Is taxed ? (1 / 0) : 0" << endl;
cout << "Price : 12.34" << endl;
test();
dumpFile(F);
cout << "----The End" << endl;
return 0;
}
This is my Product.h:
namespace sict {
class Product : public Streamable {
char sku_[MAX_SKU_LEN + 1];
char * name_;
double price_;
bool taxed_;
int quantity_;
int qtyNeeded_;
public:
Product();
Product(const char*, const char*, bool = true, double = 0, int = 0);
Product(const Product&);
virtual ~Product();
Product& operator=(const Product&);
//setters
void sku(const char*);
void price(double);
void name(const char*);
void taxed(bool);
void quantity(int);
void qtyNeeded(int);
//getters
const char* sku()const;
double price()const;
const char* name()const ;
bool taxed()const;
int quantity()const;
int qtyNeeded()const;
double cost()const;
bool isEmpty()const;
//member operators
bool operator==(const char*);
int operator+=(int);
int operator-=(int);
};
double operator+=(double, const Product&);
std::ostream& operator<<(std::ostream& ostr, const Product& p);
std::istream& operator >> (std::istream& istr, Product& p);
}
All the functions have been implemented except the last two, which are the IO operators.
Streamable class is an abstract class that has no implementations.
You did this wrong in many ways.
Best approach in your case is do it like that.
First define interfaces for stream operations, for your products:
class IStreamPrintable
{
public:
virtual std::ostream& PrintToStream(std::ostream& outStream) const = 0;
};
class IStreamReadable
{
public:
virtual std::istream& ReadFromStream(std::istream& inputStream) = 0;
};
Secondly define stream operators which will use this interfaces.
std::ostream& operator<<(std::ostream& out, const IStreamPrintable& printObject)
{
return printObject.PrintToStream(out);
}
std::istream& operator>>(std::istream& input, IStreamReadable& readObject)
{
return printObject.ReadFromStream(input);
}
Now you Product can inherit this interfaces:
class Product
: public IStreamPrintable
, public IStreamReadable
{
…
};
You do not have to implement it immediately. You can implement those methods in specific product classes SItem and it will work out of the box.
Your method virtual std::fstream& store(std::fstream& file, bool addNewLine = true) is total mess. You are passing fstream object and opening some specific file on it. This is wrong since you are unable to write multiple objects to single file. Keep there ostream object and do not change is state (do only writing), so you could cascade calls and so you could avoid hard-coding a file name.
I new a memory for my child class type stock which is inherited from base class instrument, when I try to access the second element of my array, it throws error. Things are fine when I my new array size is 1
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
class Instrument{
public:
virtual void display(){}
virtual void output(){}
virtual void readFile(){}
virtual ~Instrument(){}
};
class Stock :
public Instrument{
public:
Stock(){
}
virtual void input(){
cout << "This is stock, please input its information: ";
cin >> name >> bidPrice >> askPrice >> lastPrice >> issueExchange;
}
virtual void display(){
cout <<"This is to display stock: "<< name << " "
<< bidPrice << " "
<< askPrice << " "
<< lastPrice << " "
<< issueExchange << " "
<< endl;
}
virtual void output(){
ofstream myfile;
myfile.open("Stock.txt", ios::out | ios::app);
if (myfile.is_open()){
myfile << "This is a stock: "
<< name << " "
<< bidPrice << " "
<< askPrice << " "
<< lastPrice << " "
<< issueExchange << " "
<< endl;
}
else cout << "Unable to open file";
}
virtual void readFile(){
string line;
ifstream myfile("Stock.txt");
cout << "\nThis is file stored\n";
if (myfile.is_open())
{
while (getline(myfile, line))
{
cout << line << '\n';
}
myfile.close();
}
}
virtual ~Stock(){}
private:
char name[13];
double bidPrice;
double askPrice;
double lastPrice;
int issueExchange;
};
int main(){
const int N = 5;//it works fine if I use N=1;
Instrument *pBase = NULL;
pBase = new Stock[N];
for (int i = 0; i < N; i++){
pBase[i].input();// here throws an exception and ends the program
pBase[i].display();
pBase[i].output();
}
pBase[N - 1].readFile();
delete[] pBase;
system("pause");
return 0;
}
Polymorphism and pointer arithmetic do not mix, because the arrangement of objects within an array depends on the most-derived size, and polymorphism loses that information. The dynamic allocation is a red herring, you can see the same problem with:
Derived array[2];
Base* p = array;
printf("%p\n", &array[0]);
printf("%p\n", p);
printf("%p\n", &array[1]);
printf("%p\n", p + 1);
printf("%z\n", sizeof (array[0]));
printf("%z\n", sizeof (*p));
Note that the pointer values using array are moving forward by sizeof (Derived), but pointer arithmetic using p is moving forward by sizeof (Base) and not finding the real objects.
Generally you would fix this using an array of Base*, instead of a single Base* combined with pointer arithmetic.
Base* pp[2];
for( auto& elem : array ) pp[&elem - array] = &elem;
printf("%p\n", &array[1]);
printf("%p\n", pp[1]);
// use (*pp[1]) or pp[1]->whatever
Another option is to use an object that remembers the original type:
Derived* allocated = new Derived[N];
std::function<Base& (int)> poly = [allocated](int i){ return allocated[i]; };
and use poly(i) instead of p[i]
But warning, you CANNOT do delete [] &poly(0); because delete[] is not polymorphic either.
Using std::unique_ptr<Derived[]> and std::bind, one could arrange for automatic deallocation when the accessor object finally goes out of scope.
Although Mr.Ben's method is absolutely right, but I totally feel that his C++ and my C++ are not the same language, his is mixing some strange things here, thus according to his idea, I tried to modify my code like this.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <memory>
using namespace std;
class Instrument{
public:
virtual void display() = 0;
virtual void output() = 0;
virtual void readFile() = 0;
virtual ~Instrument(){};
};
class Stock :
public Instrument{
public:
Stock(){
cout << "This is stock, please input its information: ";
cin >> name >> bidPrice >> askPrice >> lastPrice >> issueExchange;
}
virtual void display(){
cout << "This is to display stock: " << name << " "
<< bidPrice << " "
<< askPrice << " "
<< lastPrice << " "
<< issueExchange << " "
<< endl;
}
virtual void output(){
ofstream myfile;
myfile.open("Stock.txt", ios::out | ios::app);
if (myfile.is_open()){
myfile << "This is a stock: "
<< name << " "
<< bidPrice << " "
<< askPrice << " "
<< lastPrice << " "
<< issueExchange << " "
<< endl;
}
else cout << "Unable to open file";
}
virtual void readFile(){
string line;
ifstream myfile("Stock.txt");
cout << "\nThis is file stored\n";
if (myfile.is_open())
{
while (getline(myfile, line))
{
cout << line << '\n';
}
myfile.close();
}
}
virtual ~Stock(){}
private:
string name;
double bidPrice;
double askPrice;
double lastPrice;
int issueExchange;
};
class Option :
public Instrument{
public:
Option(){
cout << "This is option, please input its information: ";
cin >> name >> uname >> bidPrice >> askPrice >> lastPrice >> contractSize >> exp;
}
virtual void display(){
cout << "This is to display option: "
<< name << " "
<< uname << " "
<< bidPrice << " "
<< askPrice << " "
<< lastPrice << " "
<< contractSize << " "
<< exp << " "
<< endl;
}
virtual void output(){
ofstream myfile;
myfile.open("Option.txt", ios::out | ios::app);
if (myfile.is_open()){
myfile << "This is an option: "
<< name << " "
<< uname << " "
<< bidPrice << " "
<< askPrice << " "
<< lastPrice << " "
<< contractSize << " "
<< exp << " "
<< endl;
}
else cout << "Unable to open file";
}
virtual void readFile(){
string line;
ifstream myfile("Option.txt");
cout << "\nThis is file stored\n";
if (myfile.is_open())
{
while (getline(myfile, line))
{
cout << line << '\n';
}
myfile.close();
}
}
virtual ~Option(){}
private:
string name;
string uname;
double bidPrice;
double askPrice;
double lastPrice;
int contractSize;
double exp;
};
class Future :
public Instrument{
public:
Future(){
cout << "This is option, please input its information: ";
cin >> name >> uname >> bidPrice >> askPrice >> lastPrice >> contractSize >> tickSize >> contractMonth;
}
virtual void display(){
cout << "This is to display option: "
<< name << " "
<< uname << " "
<< bidPrice << " "
<< askPrice << " "
<< lastPrice << " "
<< contractSize << " "
<< tickSize << " "
<< contractMonth << " "
<< endl;
}
virtual void output(){
ofstream myfile;
myfile.open("Future.txt", ios::out | ios::app);
if (myfile.is_open()){
myfile << "This is a future: "
<< name << " "
<< uname << " "
<< bidPrice << " "
<< askPrice << " "
<< lastPrice << " "
<< contractSize << " "
<< tickSize << " "
<< contractMonth << " "
<< endl;
}
else cout << "Unable to open file";
}
virtual void readFile(){
string line;
ifstream myfile("Future.txt");
cout << "\nThis is file stored\n";
if (myfile.is_open())
{
while (getline(myfile, line))
{
cout << line << '\n';
}
myfile.close();
}
}
virtual ~Future(){}
private:
string name;
string uname;
double bidPrice;
double askPrice;
double lastPrice;
int contractSize;
int tickSize;
int contractMonth;
};
int main(){
int N = 20;
//shared_ptr<Instrument> pBase[N];
vector<shared_ptr<Instrument>> pBase(N);
int i = 5;
for (i = 0; i < N; i++) pBase[i] = make_shared<Stock>();
for (i = 0; i < N; i++){
pBase[i]->display();
pBase[i]->output();
}
pBase[N - 1]->readFile();
for (i = 0; i < N; i++) pBase[i] = make_shared<Option>();
for (i = 0; i < N; i++){
pBase[i]->display();
pBase[i]->output();
}
pBase[N - 1]->readFile();
for (i = 0; i < N; i++) pBase[i] = make_shared<Future>();
for (i = 0; i < N; i++){
pBase[i]->display();
pBase[i]->output();
}
pBase[N - 1]->readFile();
system("pause");
return 0;
}
tl;dr answer: don't convert subclass array (Stock[N]) to a base class pointer (pBase). Either directly use Stock*, or create an array of pointers instead:
auto arr = new Stock*[N];
for (int i = 0; i < N; i++) {
arr[i] = new Stock();
}
// unrelated but suggested: C++11 unique_ptr is recommended:
vector<unique_ptr<Stock>> v(N);
Detailed reasons:
1) Array bracket operator is a syntactic sugar: a[b] -> *(a + b);
2) Pointer arithmetic is not polymorphic, it's always based on static types:
pBase[i] -> *(pBase+i) -> *(pBase*)((char*)pBase + sizeof(Instrument) * i);
3) This is, however, what you want:
pBase[i] -> *(pBase+i) -> *(pBase*)((char*)pBase + sizeof(Stock) * i);
4) As long as sizeof(Instrument) != sizeof(Stock), you are in trouble.
I have following code:
#include <cstring>
#include <boost/functional/hash.hpp>
#include <iostream>
int main(int argc, char **argv)
{
const char *str1 = "teststring";
// copy string
size_t len = strlen(str1);
char *str2 = new char[len+1];
strcpy(str2, str1);
// hash strings
std::cout << "str1: " << str1 << "; " << boost::hash<const char*>()(str1) << std::endl;
std::cout << "str2: " << str2 << "; " << boost::hash<const char*>()(str2) << std::endl;
delete[] str2;
return 0;
}
I always get the same hash for str1 (as expected). But str2 differs - in fact it returns a different hash every time I run the programm.
Can someone explain why?
As Linuxios suggested, it's hashing the pointer value, not the string. I did a quick test with this code:
char str1[] = "teststring";
std::cout << "str1: " << str1 << "; " << boost::hash<const char*>()(str1) << std::endl;
str1[3] = 'x';
std::cout << "str1: " << str1 << "; " << boost::hash<const char*>()(str1) << std::endl;
And here's the output. Note that the string is different but since the pointer is the same the hash matches.
str1: teststring; 158326806782903
str1: tesxstring; 158326806782903
The only change you need to make is to tell boost it's hashing a std::string and it will give you matching hashes. Your underlying data can remain char*.
std::cout << "str1: " << str1 << "; " << boost::hash<std::string>()(str1) << std::endl;
std::cout << "str2: " << str2 << "; " << boost::hash<std::string>()(str2) << std::endl;
Result:
str1: teststring; 10813257313199645213
str2: teststring; 10813257313199645213
If you actually want the hash of the string not the pointer then you can either use the boost::hash_range function or a custom loop using hash_combine and write your own hash function object. boost::hash<std::basic_string<...> > does hashes using hash_range, with has_range in turn using hash_combine.
e.g. something like this:
struct CStringHash : public std::unary_function<char const*, std::size_t> {
std::size_t operator()(char const* v) const {
std::size_t seed = 0;
for (; *v; ++v) {
boost::hash_combine(seed, *v);
}
return seed;
}
};