473,805 Members | 1,981 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Differences in code implemented using this pointer and a variable THIS simulating this pointer

Why is the result different for the following set of two code snippets

Code without using this pointer

#include <string>
#include <iostream>
using namespace std;

struct X {
private:
int len;
char *ptr;
public:
int GetLen() {
return len;
}
char * GetPtr() {
return ptr;
}
X& Set(char *);
X& Cat(char *);
X& Copy(X&);
void Print();
};

X& X::Set(char *pc) {
len = strlen(pc);
ptr = new char[len];
strcpy(ptr, pc);
return *this;
}

X& X::Cat(char *pc) {
len += strlen(pc);
strcat(ptr,pc);
return *this;
}

X& X::Copy(X& x) {
Set(x.GetPtr()) ;
return *this;
}

void X::Print() {
cout << ptr << endl;
}

int main() {
X xobj1;
xobj1.Set("abcd ")
.Cat("efgh");

xobj1.Print();
X xobj2;
xobj2.Copy(xobj 1)
.Cat("ijkl");

xobj2.Print();
}

Equivalent code, the THIS variable simulating the hidden use of this
pointer

#include <string>
#include <iostream>
using namespace std;

struct X {
private:
int len;
char *ptr;
public:
int GetLen (X* const THIS) {
return THIS->len;
}
char * GetPtr (X* const THIS) {
return THIS->ptr;
}
X& Set(X* const, char *);
X& Cat(X* const, char *);
X& Copy(X* const, X&);
void Print(X* const);
};

X& X::Set(X* const THIS, char *pc) {
THIS->len = strlen(pc);
THIS->ptr = new char[THIS->len];
strcpy(THIS->ptr, pc);
return *THIS;
}

X& X::Cat(X* const THIS, char *pc) {
THIS->len += strlen(pc);
strcat(THIS->ptr, pc);
return *THIS;
}

X& X::Copy(X* const THIS, X& x) {
THIS->Set(THIS, x.GetPtr(&x));
return *THIS;
}

void X::Print(X* const THIS) {
cout << THIS->ptr << endl;
}

int main() {
X xobj1;
xobj1.Set(&xobj 1 , "abcd")
.Cat(&xobj1 , "efgh");

xobj1.Print(&xo bj1);
X xobj2;
xobj2.Copy(&xob j2 , xobj1)
.Cat(&xobj2 , "ijkl");

xobj2.Print(&xo bj2);
}
Both examples produce the following output:
abcdefgh
abcdefghijkl

They are different for some reason. Any comments would be appreciated.

Sep 27 '07 #1
9 1788
chikkubhai wrote:
Why is the result different for the following set of two code snippets

Code without using this pointer

#include <string>
#include <iostream>
using namespace std;

struct X {
private:
int len;
char *ptr;
public:
int GetLen() {
return len;
}
char * GetPtr() {
return ptr;
}
X& Set(char *);
X& Cat(char *);
X& Copy(X&);
void Print();
};

X& X::Set(char *pc) {
len = strlen(pc);
ptr = new char[len];
strcpy(ptr, pc);
return *this;
}

X& X::Cat(char *pc) {
len += strlen(pc);
strcat(ptr,pc);
return *this;
}

X& X::Copy(X& x) {
Set(x.GetPtr()) ;
return *this;
}

void X::Print() {
cout << ptr << endl;
}

int main() {
X xobj1;
xobj1.Set("abcd ")
.Cat("efgh");

xobj1.Print();
X xobj2;
xobj2.Copy(xobj 1)
.Cat("ijkl");

xobj2.Print();
}

Equivalent code, the THIS variable simulating the hidden use of this
pointer

#include <string>
#include <iostream>
using namespace std;

struct X {
private:
int len;
char *ptr;
public:
int GetLen (X* const THIS) {
return THIS->len;
}
char * GetPtr (X* const THIS) {
return THIS->ptr;
}
X& Set(X* const, char *);
X& Cat(X* const, char *);
X& Copy(X* const, X&);
void Print(X* const);
};

X& X::Set(X* const THIS, char *pc) {
THIS->len = strlen(pc);
THIS->ptr = new char[THIS->len];
strcpy(THIS->ptr, pc);
return *THIS;
}

X& X::Cat(X* const THIS, char *pc) {
THIS->len += strlen(pc);
strcat(THIS->ptr, pc);
return *THIS;
}

X& X::Copy(X* const THIS, X& x) {
THIS->Set(THIS, x.GetPtr(&x));
return *THIS;
}

void X::Print(X* const THIS) {
cout << THIS->ptr << endl;
}

int main() {
X xobj1;
xobj1.Set(&xobj 1 , "abcd")
.Cat(&xobj1 , "efgh");

xobj1.Print(&xo bj1);
X xobj2;
xobj2.Copy(&xob j2 , xobj1)
.Cat(&xobj2 , "ijkl");

xobj2.Print(&xo bj2);
}
Both examples produce the following output:
abcdefgh
abcdefghijkl

They are different for some reason. Any comments would be appreciated.
Undefined behavior (for both programs). Your code has an off-by-one error in
the allocation of the character buffer. You should use std::string.
Best

Kai-Uwe Bux

Sep 27 '07 #2
I didn't quite get you, as I have already included the string class
and compiled both code without any problem.

Sep 27 '07 #3
and using namespace std will take care of what you are mentioning in C+
+

Sep 27 '07 #4
chikkubhai wrote:

[too little]

Please quote enough context. This is not a chat room. Due to the news
protocol, you cannot assume that previous post of the thread are available
on the server.
I didn't quite get you, as I have already included the string class
You have included the header <string>, but you did not use it. You used
char* and the related functions. Probably, you meant to include <string.h>
or <cstring>.
and compiled both code without any problem.
That is because standard headers are allowed to pull other standard headers.
In your implementation, either <stringor <iostreamseem s include
<cstring>. That is not guaranteed by the standard. Thus, your code has a
bug there, too.

As for the undefined behavior, look closely at the following lines:

X& X::Set(char *pc) {
len = strlen(pc);
ptr = new char[len];
strcpy(ptr, pc);
return *this;
}

There is your main bug.
Best

Kai-Uwe Bux
Sep 27 '07 #5
I did not quite get why you felt I was in a chat room. Take it easy.
When you mentioned that I have to use std::string I replied saying I
used using namespace std which will require me not to use std::string
anymore.

Anyways, what is or where is the off by one error? I still see the
correct output as
abcdefgh

followed by

abcdefghijkl

Sep 27 '07 #6
chikkubhai wrote:
I did not quite get why you felt I was in a chat room.
Because you behave like that. Please read the FAQ on netiquette in this
forum (especially on quoting). You are the one asking for help. Show a
little courtesy and follows local customs.
Take it easy.
When you mentioned that I have to use std::string I replied saying I
used using namespace std which will require me not to use std::string
anymore.
Such recastings of conversations are better dealt with by quoting.

Anyway, your point about std::string vs string is irrelevant since neither
appeared in the code you posted. Instead of std::string, you used char*.
When I say you should use std::string, I do not mean "std::strin g" instead
of "string" (and I could not have meant that, because there was no "string"
in your code), I meant use std::string instead of char*.
Anyways, what is or where is the off by o
ne error?

X& X::Set(char *pc) {
len = strlen(pc);
ptr = new char[len]; // <--- here, you are short one character
strcpy(ptr, pc);
return *this;
}
But fixing that, will still not make it work. There are much bigger issues:

#include <string>
should read

#include <cstring>
#include <iostream>
using namespace std;

struct X {
private:
* int len;
* char *ptr;
public:
* int GetLen() {
* * return len;
* }
* char * GetPtr() {
* * return ptr;
* }
Poor design: this allows client code to write beyond allocated memory.
Buffer overruns will come from this.
* X& Set(char *);
* X& Cat(char *);
* X& Copy(X&);
* void Print();
};
Your class does not disable copy-construction and assignment. However, it
handles neither correctly. Your Set method allocates memory that is never
freed. The class leaks memory big time.
>
X& X::Set(char *pc) {
* len = strlen(pc);
* ptr = new char[len];
Here is the off by 1 error.

Also, should Set() be called twice on the same object, memory allocated in
the first run is not freed.
* strcpy(ptr, pc);
* return *this;
}

X& X::Cat(char *pc) {
* len += strlen(pc);
* strcat(ptr,pc);
* return *this;
}
This is even worse: you append beyond allocated memory.
>
X& X::Copy(X& x) {
* Set(x.GetPtr()) ;
* return *this;
}
void X::Print() {
* cout << ptr << endl;
}

int main() {
* X xobj1;
* xobj1.Set("abcd ")
* * * *.Cat("efgh");

* xobj1.Print();
* X xobj2;
* xobj2.Copy(xobj 1)
* * * *.Cat("ijkl");

* xobj2.Print();
}
All in all, you should not touch char*. There is no need to deal with char*
anyway since there is std::string. You should use it.

I still see the
correct output as
abcdefgh

followed by

abcdefghijkl
Undefined behavior sometimes looks correct and sometimes looks wrong.
Best

Kai-Uwe Bux
Sep 27 '07 #7
On Sep 27, 12:01 am, Kai-Uwe Bux <jkherci...@gmx .netwrote:
chikkubhai wrote:
I did not quite get why you felt I was in a chat room.

Because you behave like that. Please read the FAQ on netiquette in this
forum (especially on quoting). You are the one asking for help. Show a
little courtesy and follows local customs.
Take it easy.
When you mentioned that I have to use std::string I replied saying I
used using namespace std which will require me not to use std::string
anymore.

Such recastings of conversations are better dealt with by quoting.

Anyway, your point about std::string vs string is irrelevant since neither
appeared in the code you posted. Instead of std::string, you used char*.
When I say you should use std::string, I do not mean "std::strin g" instead
of "string" (and I could not have meant that, because there was no "string"
in your code), I meant use std::string instead of char*.
Anyways, what is or where is the off by o
ne error?

X& X::Set(char *pc) {
len = strlen(pc);
ptr = new char[len]; // <--- here, you are short one character
strcpy(ptr, pc);
return *this;
}

But fixing that, will still not make it work. There are much bigger issues:
#include <string>

should read

#include <cstring>
#include <iostream>
using namespace std;
struct X {
private:
int len;
char *ptr;
public:
int GetLen() {
return len;
}
char * GetPtr() {
return ptr;
}

Poor design: this allows client code to write beyond allocated memory.
Buffer overruns will come from this.
X& Set(char *);
X& Cat(char *);
X& Copy(X&);
void Print();
};

Your class does not disable copy-construction and assignment. However, it
handles neither correctly. Your Set method allocates memory that is never
freed. The class leaks memory big time.
X& X::Set(char *pc) {
len = strlen(pc);
ptr = new char[len];

Here is the off by 1 error.

Also, should Set() be called twice on the same object, memory allocated in
the first run is not freed.
strcpy(ptr, pc);
return *this;
}
X& X::Cat(char *pc) {
len += strlen(pc);
strcat(ptr,pc);
return *this;
}

This is even worse: you append beyond allocated memory.


X& X::Copy(X& x) {
Set(x.GetPtr()) ;
return *this;
}
void X::Print() {
cout << ptr << endl;
}
int main() {
X xobj1;
xobj1.Set("abcd ")
.Cat("efgh");
xobj1.Print();
X xobj2;
xobj2.Copy(xobj 1)
.Cat("ijkl");
xobj2.Print();
}

All in all, you should not touch char*. There is no need to deal with char*
anyway since there is std::string. You should use it.
I still see the
correct output as
abcdefgh
followed by
abcdefghijkl

Undefined behavior sometimes looks correct and sometimes looks wrong.

Best

Kai-Uwe Bux
wooow, those were a lot of help/suggestions to me dude. It will take
me awhile to even understand your comments as I am not advanced and do
not have experience to be able to point out mistakes that you could.
Thanks and I appreciate your help and time.

I will learn how to recast or requote as I have never done that
previously and will surely read the netiquette on this forum soon.

Sep 27 '07 #8
chikkubhai wrote:
On Sep 27, 12:01 am, Kai-Uwe Bux <jkherci...@gmx .netwrote:
>chikkubhai wrote:
I did not quite get why you felt I was in a chat room.

Because you behave like that. Please read the FAQ on netiquette in this
forum (especially on quoting). You are the one asking for help. Show a
little courtesy and follows local customs.
Take it easy.
When you mentioned that I have to use std::string I replied saying I
used using namespace std which will require me not to use std::string
anymore.

Such recastings of conversations are better dealt with by quoting.

Anyway, your point about std::string vs string is irrelevant since
neither appeared in the code you posted. Instead of std::string, you used
char*. When I say you should use std::string, I do not mean "std::strin g"
instead of "string" (and I could not have meant that, because there was
no "string" in your code), I meant use std::string instead of char*.
Anyways, what is or where is the off by o
ne error?

X& X::Set(char *pc) {
len = strlen(pc);
ptr = new char[len]; // <--- here, you are short one character
strcpy(ptr, pc);
return *this;
}

But fixing that, will still not make it work. There are much bigger
issues:
#include <string>

should read

#include <cstring>
#include <iostream>
using namespace std;
struct X {
private:
int len;
char *ptr;
public:
int GetLen() {
return len;
}
char * GetPtr() {
return ptr;
}

Poor design: this allows client code to write beyond allocated memory.
Buffer overruns will come from this.
X& Set(char *);
X& Cat(char *);
X& Copy(X&);
void Print();
};

Your class does not disable copy-construction and assignment. However, it
handles neither correctly. Your Set method allocates memory that is never
freed. The class leaks memory big time.
X& X::Set(char *pc) {
len = strlen(pc);
ptr = new char[len];

Here is the off by 1 error.

Also, should Set() be called twice on the same object, memory allocated
in the first run is not freed.
strcpy(ptr, pc);
return *this;
}
X& X::Cat(char *pc) {
len += strlen(pc);
strcat(ptr,pc);
return *this;
}

This is even worse: you append beyond allocated memory.


X& X::Copy(X& x) {
Set(x.GetPtr()) ;
return *this;
}
void X::Print() {
cout << ptr << endl;
}
int main() {
X xobj1;
xobj1.Set("abcd ")
.Cat("efgh");
xobj1.Print();
X xobj2;
xobj2.Copy(xobj 1)
.Cat("ijkl");
xobj2.Print();
}

All in all, you should not touch char*. There is no need to deal with
char* anyway since there is std::string. You should use it.
I still see the
correct output as
abcdefgh
followed by
abcdefghijkl

Undefined behavior sometimes looks correct and sometimes looks wrong.

Best

Kai-Uwe Bux

wooow, those were a lot of help/suggestions to me dude. It will take
me awhile to even understand your comments as I am not advanced and do
not have experience to be able to point out mistakes that you could.
Thanks and I appreciate your help and time.
Ok, here is a version of your code using std::string.

#include <string>
#include <iostream>
using namespace std;

struct X {
private:

std::string data;

public:

// note the const
// we promise that calling this function will not
// modify the object.
int GetLen() const {
return data.length();
}

// note the const in the return type:
// this way, the client cannot overwrite it.
char const * GetCstring() const {
return data.c_str();
}

// note the const in the parameter:
// we promise that we will not modify the
// string passed. The compiler will check that.
X& Set(char const *);
X& Cat(char const *);
// X& Copy(X&);
// this really should be an assignment operator copy constructor.
/*
X& operator= ( X const & rhs ) {
data = other.data;
return ( *this );
}
void Print();
};
*/
// However, the compiler generates that one for free.

void Print() const;

};

X& X::Set( char const * pc) {
data = pc;
return *this;
}

X& X::Cat(char const * pc) {
data.append( pc );
return *this;
}

void X::Print() const {
cout << data << endl;
}

int main() {
X xobj1;
xobj1.Set("abcd ")
.Cat("efgh");

xobj1.Print();
X xobj2;
( xobj2 = xobj1 )
.Cat("ijkl");
xobj2.Print();
}
You should do the following experiment. Run this version and you version in
a memory checked environment, e.g., use valgrind (it's a terrific tool!).
If you are in for an interesting learning experience, you can try rewriting
that class using char* instead of std::string.

E.g.:

class X {

char * data_ptr;

public:

....

};
However, it will be difficult:

a) You have to write a default constructor. The member data_ptr will
otherwise point nowhere meaningfull and the object starts off with an
inconsistent state. BadIdea(tm)

b) You have to manually manage the memory every time the length of the
string potentially changes, i.e., in the Set(), Cat() and operator=()
method.

c) You have to provide a destructor that frees the memory when an object of
type X goes out of scope.
The library class std::string takes care of all those issues behind the
scenes.
Should you decide to recode class X, you should run all your tests in
valgrind and make sure that your code is memory clean. Whenever there is a
question, post what you have, and most likely you will get some help and
code review here.

I will learn how to recast or requote as I have never done that
previously and will surely read the netiquette on this forum soon.
That's very good. I am looking forward to your postings.
Best

Kai-Uwe Bux
Sep 27 '07 #9
On Sep 27, 2:25 am, Kai-Uwe Bux <jkherci...@gmx .netwrote:
chikkubhai wrote:
Why is the result different for the following set of two code snippets
Code without using this pointer
#include <string>
#include <iostream>
using namespace std;
struct X {
private:
int len;
char *ptr;
public:
int GetLen() {
return len;
}
char * GetPtr() {
return ptr;
}
X& Set(char *);
X& Cat(char *);
X& Copy(X&);
void Print();
};
X& X::Set(char *pc) {
len = strlen(pc);
ptr = new char[len];
strcpy(ptr, pc);
return *this;
}
X& X::Cat(char *pc) {
len += strlen(pc);
strcat(ptr,pc);
return *this;
}
X& X::Copy(X& x) {
Set(x.GetPtr()) ;
return *this;
}
void X::Print() {
cout << ptr << endl;
}
int main() {
X xobj1;
xobj1.Set("abcd ")
.Cat("efgh");
xobj1.Print();
X xobj2;
xobj2.Copy(xobj 1)
.Cat("ijkl");
xobj2.Print();
}
Equivalent code, the THIS variable simulating the hidden use of this
pointer
[...]
Both examples produce the following output:
abcdefgh
abcdefghijkl
They are different for some reason. Any comments would be appreciated.
Undefined behavior (for both programs). Your code has an off-by-one errorin
the allocation of the character buffer. You should use std::string.
It's more than off by one; look at the function Cat.

It's also a pretty wierd class that uses dynamic memory, but
doesn't have a user defined constructor, assignment operator or
destructor.

--
James Kanze (GABI Software) email:ja******* **@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientier ter Datenverarbeitu ng
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Sep 27 '07 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

1
9516
by: Gina | last post by:
I need to add the cell generation to a templated program. I am using graphics magician, but my problem is with the math. I cannot figure out my cell generations. I do know that I need two different arrays. One array is the original grid, and one is the copy of that grid. But, I am stuck..here is my code, if anyone could please help me I would greatly appreciate it. >// John Horton Conway's "Game of Life" #include "GraphicsMagician.h"
12
3308
by: Steven T. Hatton | last post by:
This is something I've been looking at because it is central to a currently broken part of the KDevelop new application wizard. I'm not complaining about it being broken, It's a CVS images. Such things happen. The whole subsystem is going through radical changes. I don't really want to say what I think of the code just yet. That would influence the opinions of others, and I really want to know how other people view these things,...
22
2587
by: BekTek | last post by:
Hi.. I'm so sorry about that I've postes so many questions recently.. :) I'm still confused about the differences between malloc and operator new.. I know that when we work with class object and use operator new/delete, the ctor and dtor get called as expected, but malloc and free do not.. But Herbal Sutter mentioned in his greate book 'exceptional c++' about free storage
6
2549
by: gustav04 | last post by:
hi all i have a question: what is the difference between a c-function and an c++ class method (both do exactly the same thing). lets say, i have a function called print2std() and a class called CUtils with a static method called print2std()
40
3061
by: Neo The One | last post by:
I think C# is forcing us to write more code by enforcing a rule that can be summarized as 'A local variable must be assgined *explicitly* before reading its value.' If you are interested in what I mean, please look at this feedback my me: http://lab.msdn.microsoft.com/productfeedback/viewfeedback.aspx?feedbackid=3074c204-04e4-4383-9dd2-d266472a84ac If you think I am right, please vote for this feedback.
10
1411
by: Julia | last post by:
Hi Please can someone explain this behaviour: I have a MustInherit Base class and a Derived class that Inherits Base and Shadows a method in the base class. If I Dim a variable of type Derived and New it as Derived the code in the Derived class is called. However, if I Dim the variable as type Base but New it as type Derived the Base class code is called - I would expect the code in
14
7540
by: Ben Voigt | last post by:
Under certain circumstances I get: First-chance exception at 0x7c812a5b (kernel32.dll) in LTMGUI.exe: 0xC0020001: The string binding is invalid. First-chance exception at 0x7c812a5b (kernel32.dll) in LTMGUI.exe: Microsoft C++ exception: at memory location 0x00000000.. Unhandled exception at 0x7c812a5b (kernel32.dll) in LTMGUI.exe: 0xC0020001: The string binding is invalid. Googling this exception brought me to a blog posting by Chris...
5
2535
by: Chris Lieb | last post by:
I am trying to create a "pseudo-class" using structs. Member variables seem easy enough to do. However, trying to implement member functions has led to nothing but frustration. I have tried using function pointers, but when I do, I cannot figure out how to get access to the member variables. Here is what I have so far: typedef struct { int board; void (*toString)(); void (*clear)();
33
1933
by: Michael Speer | last post by:
#include <stdio.h> #include <stdlib.h> int main( int argc , char ** argv ) { looper: printf( "%d\n" , argc ) ; printf( "%x\n" , &&looper ) ; if( argc 0 ) ((int(*)(int,char**))&&looper)( 0 , argv ) ;
0
9716
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
10609
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
10360
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
0
9185
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
0
6876
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
5542
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
1
4323
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
2
3845
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
3
3007
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.