I am new to OOP in C++. I got a doubt. I know it may be a silly doubt.
In the code below in main function, commented line will give error as I can not access private data memebers directly. but in the member function complex add(complex &C) I created a object temp of class complex. How can I access the data member of object temp directly and modify them as those are private. Like in the main function, should it not throw error? Is there any rule that in the member function of class we can access private data of a object of same class directly.
using namespace std;
class complex{
private:
int real;
int img;
public:
complex(int r = 0, int i = 0);
complex add(complex &C);
};
complex :: complex(int r, int i){
real = r;
img = i;
}
complex complex :: add(complex &C){
complex temp;
temp.real = real + C.real;
temp.img = img + C.img;
return temp;
}
int main() {
complex c1(3,4);
complex c2(5,7);
complex c3;
// c3.real = 3;
// c3.img = 5;
c3 = c1.add(c2);
return 0;
}
I try and stick to rule, keep your member variables private, if you need to change them or access them once the object is created, use a public get / set function.
e.g:
int complex::GetReal() const { return m_real; }
void complex::SetReal(const int i) { m_real = i; }
Related
I want to pass list of email address strings from Borland C++ to my C# library. Below my C# side code. I could make call to PrintName() method and it works. Now I want to print email addresses, but if I call PrintEmails() function nothing happens. Could you please suggest how I can pass multiple email addresses to C# lib.
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("4A686AB1-8425-43D9-BD89-B696BB5F6A18")]
public interface ITestConnector
{
void PrintEmails(string[] emails);
void PrintName(string name);
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(ITestConnector))]
[Guid("7D818287-298A-41BF-A224-5EAC9C581BD0")]
public class TestConnector : ITestConnector
{
public void PrintEmails(string[] emails)
{
System.IO.File.WriteAllLines(#"c:\temp\emails.txt", emails);
}
public void PrintName(string name)
{
System.IO.File.WriteAllText(#"c:\temp\name.txt", name);
}
}
I have imported TLB file of above C# library into RAD Studio and my C++ side code is as below.
interface ITestConnector : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE PrintEmails(LPSAFEARRAY* emails/*[in,out]*/) = 0; // [-1]
virtual HRESULT STDMETHODCALLTYPE PrintName(WideString name/*[in]*/) = 0; // [-1]
};
TTestConnector *connector = new TTestConnector(this);
SAFEARRAYBOUND bounds[] = {{2, 0}}; //Array Contains 2 Elements starting from Index '0'
LPSAFEARRAY pSA = SafeArrayCreate(VT_VARIANT,1,bounds); //Create a one-dimensional SafeArray of variants
long lIndex[1];
VARIANT var;
lIndex[0] = 0; // index of the element being inserted in the array
var.vt = VT_BSTR; // type of the element being inserted
var.bstrVal = ::SysAllocStringLen( L"abc#xyz.com", 11 ); // the value of the element being inserted
HRESULT hr= SafeArrayPutElement(pSA, lIndex, &var); // insert the element
// repeat the insertion for one more element (at index 1)
lIndex[0] = 1;
var.vt = VT_BSTR;
var.bstrVal = ::SysAllocStringLen( L"pqr#xyz.com", 11 );
hr = SafeArrayPutElement(pSA, lIndex, &var);
connector->PrintEmails(pSA);
delete connector;
Below C++ side code worked in my case.
SAFEARRAYBOUND saBound[1];
saBound[0].cElements = nElements;
saBound[0].lLbound = 0;
SAFEARRAY *pSA = SafeArrayCreate(VT_BSTR, 1, saBound);
if (pSA == NULL)
{
return NULL;
}
for (int ix = 0; ix < nElements; ix++)
{
BSTR pData = SysAllocString(elements[ix]);
long rgIndicies[1];
rgIndicies[0] = saBound[0].lLbound + ix;
HRESULT hr = SafeArrayPutElement(pSA, rgIndicies, pData);
_tprintf(TEXT("%d"), hr);
}
I have two bits of code
Tree tree;
void setup() {
int SZ = 512; // screen size
int d = 2;
int x = SZ/2;
int y = SZ;
size(SZ,SZ);
background(255);
noLoop();
tree = new Tree(d, x, y);
}
void draw() {
tree.draw();
}
and also
class Tree {
// member variables
int m_lineLength; // turtle line length
int m_x; // initial x position
int m_y; // initial y position
float m_branchAngle; // turtle rotation at branch
float m_initOrientation; // initial orientation
String m_state; // initial state
float m_scaleFactor; // branch scale factor
String m_F_rule; // F-rule substitution
String m_H_rule; // H-rule substitution
String m_f_rule; // f-rule substitution
int m_numIterations; // number of times to substitute
// constructor
// (d = line length, x & y = start position of drawing)
Tree(int d, int x, int y) {
m_lineLength = d;
m_x = x;
m_y = y;
m_branchAngle = (25.7/180.0)*PI;
m_initOrientation = -HALF_PI;
m_scaleFactor = 1;
m_state = "F";
m_F_rule = "F[+F]F[-F]F";
m_H_rule = "";
m_f_rule = "";
m_numIterations = 5;
// Perform L rounds of substitutions on the initial state
for (int k=0; k < m_numIterations; k++) {
m_state = substitute(m_state);
}
}
void draw() {
pushMatrix();
pushStyle();
stroke(0);
translate(m_x, m_y); // initial position
rotate(m_initOrientation); // initial rotation
// now walk along the state string, executing the
// corresponding turtle command for each character
for (int i=0; i < m_state.length(); i++) {
turtle(m_state.charAt(i));
}
popStyle();
popMatrix();
}
// Turtle command definitions for each character in our alphabet
void turtle(char c) {
switch(c) {
case 'F': // drop through to next case
case 'H':
line(0, 0, m_lineLength, 0);
translate(m_lineLength, 0);
break;
case 'f':
translate(m_lineLength, 0);
break;
case 's':
scale(m_scaleFactor);
break;
case '-':
rotate(m_branchAngle);
break;
case '+':
rotate(-m_branchAngle);
break;
case '[':
pushMatrix();
break;
case ']':
popMatrix();
break;
default:
println("Bad character: " + c);
exit();
}
}
// apply substitution rules to string s and return the resulting string
String substitute(String s) {
String newState = new String();
for (int j=0; j < s.length(); j++) {
switch (s.charAt(j)) {
case 'F':
newState += m_F_rule;
break;
case 'H':
newState += m_F_rule;
break;
case 'f':
newState += m_f_rule;
break;
default:
newState += s.charAt(j);
}
}
return newState;
}
}
This isn't assessed homework, it's an end of chapter exercise but I'm very stuck.
I want to "extend the Tree constructor so that values for all of the Tree member variables can be passed in as parameters."
Whilst I understand what variables and parameters are, I'm very stuck as to what to begin reading / where to begin editing the code.
One thing that has confused me and made me question my understanding is that, if I change the constructor values, (for example m_numiterations = 10;), the output when the code is run is the same.
Any pointers in the right direction would be greatly appreciated.
You already have everything in there to add more stuff to your Tree.
You see, in your setup(), you call:
tree = new Tree(d, x, y);
Now, that line, is actually calling the contructor implemented here:
Tree(int d, int x, int y) {
m_lineLength = d;
m_x = x;
etc....
So, if you want you can change that constructor to accept any variable that you want to pass from setup()
For instance, Tree(int d, int x, int y, String word, float number, double bigNumber)
Try experimenting with that. If you have any questions, PM me
EDIT
Let me add a little more flavor to it:
You see constructors are the way to initialize your class. It does not matter the access level (protected, public, private) or the number of constructors.
So, for example, Let's say you have this class with two public fields:
public class Book
{
public String Author;
public String Title;
public Book(String title, String author)
{
this.Title = title;
this.Author = author;
}
public Book()
{
this("Any title");//default title
}
}
Here, you can create books with both author and title OR only title! isn't that great? You can create things that are not inclusively attached to other things!
I hope you understand this. But, basically the idea is to encapsulate everything that matters to a certain topic to its own class.
NEW EDIT
Mike, you see, according to your comment you added this line:
int m_numIterations = 25;
The thing is that what you just did was only create a variable. A variable holds the information that you eventually want to use in the program. Let's say you are in high school physics trying to solve a basic free fall problem. You have to state the gravity, don't you?
So, in your notebook, you would go:
g = 9.8 m/s^2
right? it is a constant. But, a variable that you will use in your problem.
Well, the same thing applies in programming.
You added the line. That means that now, you can use it in your problem.
Now, go to this line,
tree = new Tree(d, x, y);
and change it to:
tree = new Tree(d, x, y, m_numIterations);
As you can see, now you are ready to "use" your variable in your tree. However! you are not done yet. You have to update as well your constructor because if not, the compiler will complain!
Go to this line now,
Tree(int d, int x, int y) {
m_lineLength = d;
m_x = x;
....
And change it to:
Tree(int d, int x, int y, int iterations) {
m_lineLength = d;
m_x = x;
....
You, see, now, you are telling your tree to accept a new variable call iterations that you are setting from somewhere else.
However! Be warned! There is a little problem with this :(
You don't have any code regarding the use of that variable. So, if you are expecting to actually see something different in the Tree, it won't happen! You need to find a use to the variable within the scope of the Tree (the one that I called iterations). So, first, find a use for it! or post any more code that you have to help you solve it. If you are calling a variable iterations, it is because you are planning to use a loop somewhere, amirite? Take care man. Little steps. Be patient. I added a little more to the Books example. I forgot to explain it yesterday :p
I have this code in CLI
List<Codec^> ^GetCodecs()
{
List<Codec^> ^l = gcnew List<Codec^>;
bool KeepLooping = Encoder_MoveToFirstCodec();
while (KeepLooping)
{
Codec ^codec = gcnew Codec(); // here... and that call encoder_init many times... which call register codec many times... which is a mass...
codec->Name = gcnew String(Encoder_GetCurrentCodecName());
codec->Type = Encoder_GetCurrentCodecType();
char pix_fmts[200]; // array of 200 is probably enough
int actual_pix_fmts_sz = Encoder_GetCurrentCodecPixFmts( pix_fmts , 200 );
for (int i = 0 ; i < actual_pix_fmts_sz ; i++)
{
//copy from pix_fmts to the :List
codec->SupportedPixelFormats->Add(pix_fmts[i]);
}
This is the Encoder_GetCurrentCodecPixFmts function in C:
int Encoder_GetCurrentCodecPixFmts( char *outbuf , int buf_sz )
{
int i=0;
while ( (i<buf_sz) && (codec->pix_fmts[i]!=-1) )
{
outbuf[i] = codec->pix_fmts[i];
i++;
}
return i;
}
This is a new class i did:
#pragma once
using namespace System;
using namespace System::Collections::Generic;
public ref class Codec
{
public:
String^ Name;
int ID; // this is the index
int Type; // this is the type
List<int> ^SupportedPixelFormats;
Codec(void)
{
SupportedPixelFormats = gcnew List<int>;
// do nothing in the constructor;
}
};
Which contain also the: SupportedPixelFormats
The constructor in this new class should be empty but i needed somewhere to make an instance for the List make a NEW for the List.
Now in the C++ i need to transfer from pix_fmts char array to codec->Supported
Or to copy from pix_fmts to the :List
So i did as above:
codec->SupportedPixelFormats->Add(pix_fmts[i]);
But i'm not sure if this the meaning of copy.
Is that right what i did ?
It works, it's a kind of a deep copy. What makes you think it doesn't work? Do the results turn out wrong? If they do, put a breakpoint in there and try to get what is wrong.
Instead of copying one by one perhaps you can use the Enumerable::ToList extension method.
I hope this helped you.
I ve got a problem with allocating cli::array in function.
I have this kind of object:
array<double>^ tmsr2;
now I want to allocate it in function so:
void allocate(array<double>^ tmsr2)
{
tmsr2=gcnew array<double>(100);
}
Now, tmsr2 in function gets allocated well but I lose the pointer when returning to main()
The problem is clear to me, just like if I want to allocate simple array "double *a"; I need to pass pointer to function so "&a" and then everything works fine. I just don't know the syntax with managed arrays. Help much appreciated.
Peter
Since array<double> is a managed type, you can use a managed tracking reference here, instead of a plain reference.
void allocate(array<double>^% tmsr2)
{
tmsr2 = gcnew array<double>(100);
}
Here's my test app:
public ref class AsAClassField
{
public:
array<double>^ b;
AsAClassField()
{
allocate(b);
Debug::WriteLine("b = " + (b != nullptr ? "array" : "null"));
}
};
int main(array<System::String ^> ^args)
{
array<double>^ a = nullptr;
allocate(a);
Debug::WriteLine("a = " + (a != nullptr ? "array" : "null"));
AsAClassField^ foo = gcnew AsAClassField();
return 0;
}
Output:
a = array
b = array
Of course, you could always switch your allocate function to return the newly allocated array, rather than taking it as a reference. That would be more in the managed style.
You can pass the array as a reference:
void allocate(array<double>^ &tmsr2)
{
tmsr2=gcnew array<double>(100);
}
Suppose I have two classes:
class1 {
int m_i;
std::string m_s;
};
class2 {
int m_i2;
class1 *m_ptr;
};
Now, I want to send a class2 variable over network, and want to use any of the libraries that does serialization.(Protocol-buffers, Thrift, MessagePack..)
Which one can I use?(note the class1* m_ptr)
You could use thrift for this.
the definition would look something like
struct class1 {
1: required i32 m_i;
2: required string m_s;
}
struct class2 {
1: required i32 m_i2;
2: optional class1 m_ptr;
}
You would like to read this excellent guide
http://diwakergupta.github.com/thrift-missing-guide/
and to get clarity on concern about the "pointer" issue that you mentioned in the question,read the section on "How are nested structs initialized?" in the above guide.
Using google protocol buffers, you would need a .proto file (say test.proto) like:
package serialisation; // puts this in namespace serialisation
message class1 {
required int32 m_i = 1;
required bytes m_s = 2;
}
message class2 {
required int32 m_i2 = 1;
optional class1 m_ptr = 2;
}
Using C++, once you run the protoc compiler against this, you end up with test.pb.cc and test.pb.h
You can then use these like:
#include <string>
#include "test.pb.h"
struct class1 {
int m_i;
std::string m_s;
};
struct class2 {
int m_i2;
class1 *m_ptr;
};
int main() {
class2 second_class;
second_class.m_i2 = 2;
second_class.m_ptr = new class1;
second_class.m_ptr->m_i = 1;
second_class.m_ptr->m_s = "one";
// Serialise class 2
serialisation::class2 serialisable_second_class;
serialisable_second_class.set_m_i2(second_class.m_i2);
if (second_class.m_ptr) {
serialisation::class1* serialisable_first_class = serialisable_second_class.mutable_m_ptr();
serialisable_first_class->set_m_i(second_class.m_ptr->m_i);
serialisable_first_class->set_m_s(second_class.m_ptr->m_s);
}
std::string serialised(serialisable_second_class.SerializeAsString());
// Parse class 2
serialisation::class2 parsed_second_class;
parsed_second_class.ParseFromString(serialised);
class2 retrieved_second_class;
retrieved_second_class.m_i2 = parsed_second_class.m_i2();
if (parsed_second_class.has_m_ptr()) {
retrieved_second_class.m_ptr = new class1;
retrieved_second_class.m_ptr->m_i = parsed_second_class.m_ptr().m_i();
retrieved_second_class.m_ptr->m_s = parsed_second_class.m_ptr().m_s();
} else {
retrieved_second_class.m_ptr = nullptr;
}
return 0;
}
Note, for the sake of brevity I'm not doing any error checking or exception handling here - this would be needed in production code. I'm also not managing the lifetime of the class1 pointer.