473,756 Members | 2,378 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

questions about virtual functions and abstract classes

Hello,

If I have a class that has virtual but non-pure declarations, like

class A{
virtual void f();
};

Then is A still an abstract class? Do I have to have "virtual void
f() = 0;" instead? I think declaring a function as "=0" is the same
as not giving its definition, is this right?

In addition, if I have a virtual destructor for an abstract class A,
then why do I still have to define it? For example, if I have

class B{
virtual ~B();
virtual void f() = 0;
};

Then my compiler still asks me to define ~B(). However, I will never
create an object of type B!

Additionally, I don't think I can declare virtual constructor, but it
seems I can't make a pure constructor like

class C{
C() = 0;
virtual void f()=0;
};

Again, if I never make an object of type C, why am I not allowed to
declare the constructor pure?

Moreover, is there any function or operator that can't be virtual,
just like constructors?

Thanks!
Jess

Jun 19 '07
17 3545
Thanks a lot!

On Jun 20, 6:20 pm, James Kanze <james.ka...@gm ail.comwrote:
On Jun 19, 2:32 pm, Jess <w...@hotmail.c omwrote:
If I have a class that has virtual but non-pure declarations, like
class A{
virtual void f();
};
Then is A still an abstract class?

No.
Do I have to have "virtual void
f() = 0;" instead?

If you want the class to be abstract. Doing so has other
repercusions, however.

In practice, "abstract" represents a restriction on what can be
done with the class. It's never something you absolutely need
from a programming point of view; the fact that a class is
abstract is rather a consequence of the fact that you need pure
virtual functions.
I think declaring a function as "=0" is the same
as not giving its definition, is this right?

Not at all. The rules are far more subtle. Declaring a
function pure virtual (i.e. appending "= 0" to its declaration)
has several effects:

1. it makes the class abstract,

2. it means that anytime dynamic lookup of the function
resolves to the function in this class, you have undefined
behavior, and
If a function is pure virtual but I still define it, would the
compiler put the function's address into vtbl? If the compiler puts
the addr into the vtble, then I think it wouldn't cause an undefined
behaviour when this function is called.
3. it means that you are not required to provide a definition
of the function unless it is explicitly called.
By "explicitly called", do you mean the example like "Base::f()"
below?
Concerning these points;

1. You cannot create an instance of an abstract class.
Attention, however. This does not mean that there are never
objects which have the dynamic type of the class. During
construction and destruction, the dynamic type evolves to
alway be that of the constructor or destructor. So if you
call a pure virtual function (directly or indirectly) while
executing the constructor or the destructor, the dynamic
resolution will result in the pure virtual function. Which
leads to point 2:
I don't quite understand this...What is "dynamic types evolve to be
that of constructor or destructor" and how can a pure virtual function
be called in a constructor or destructor? I had an experiment

#include<iostre am>

using namespace std;

class A{
public:
virtual ~A(){};
virtual void f()=0;
};

void A::f(){cout << "A::f()" << endl;}

class B:public A{
public:
int x;
B():x(10){f();}
void f(){cout << "B::f()" << endl;}
};
int main(){
B b;
return 0;
}

In the constructor B(), I called the virtual function f(). I remember
the book Effective C++ says if I call a virtual function (f()) in a
constructor (B()), then the function won't be resolved to B::f()
because the object is not yet of type B. Instead A::f() is called.
However, when I ran my program, I got B::f(). What happened?
>
2. It's undefined behavior. From a quality of implementation
point of view, I would expect the program to abort, with an
error message, and this is, in fact, what most compilers do.
But it's not guaranteed, and you cannot count on it.
Literally anything can happen, and you must absolutely avoid
the case.

3. Normally, if a function is virtual, it is considered to be
used, regardless of whether you ever actually call it or
not. And if a function is used, it must defined, somewhere.
If a function is pure virtual, however, it is not
automatically considered used, and so a definition is only
required if you actually do use it; since it cannot be found
by dynamic lookup (point 2), the only way to use it is to
call it explicitly, i.e. Base::f().
So even if the pure virtual function is defined, its addr is not put
into vtbl? Is the addr not stored at all, not in vtbl, not anywhere
else? By the way, is this why my program above got B::f() instead of
A::f()? However, even if A::f() isn't in the vtbl, I thought the
object being constructed isn't of type B yet, so its B::f() shouldn't
be called.

Note that the compiler
will generate such calls to the same function in the base
class implicitly in constructors, the destructor, and in a
compiler generated assignment operator.
Do you mean if I have a derived class B, base class A, then in B's
constructor, the compiler generates A::A(), in B's destructor it
generates A::~A() etc?

Thanks,
Jess

Jun 20 '07 #11
On Jun 20, 6:30 pm, James Kanze <james.ka...@gm ail.comwrote:
In general, I've not heard the name pure abstract class, but
rather interface, for such classes. Probably an influence from
Java, which has a special keyword for such classes, and allows
multiple inheritance from them (but not from other classes). In
practice, of course, most real interfaces will want to define a
contract, which means that the public functions will be
non-virtual, forwarding to private (or protected) pure virtual
functions.
Why are public functions non-virtual? I thought an interface-
implementation would look like

class A{
public:
virtual void f() = 0;
virtual void g() = 0;
};

class B:public A{
public:
void f();
void g();
};

void B::f(){}
void B::g(){}

In other words, A declares a set of virtual functions and B inherits
from A and implements the virtual functions. Then a client program
can use A& or A* to do polymorphic operations. Is this not right? In
addition, when do we need to "forward to private (or protected) pure
virtual function"?

Thanks,
Jess

Jun 20 '07 #12
On 20 Jun, 12:12, Jess <w...@hotmail.c omwrote:
On Jun 20, 6:30 pm, James Kanze <james.ka...@gm ail.comwrote:
In general, I've not heard the name pure abstract class, but
rather interface, for such classes. Probably an influence from
Java, which has a special keyword for such classes, and allows
multiple inheritance from them (but not from other classes). In
practice, of course, most real interfaces will want to define a
contract, which means that the public functions will be
non-virtual, forwarding to private (or protected) pure virtual
functions.

Why are public functions non-virtual?
http://www.gotw.ca/publications/mill18.htm

Gavin Deane

Jun 20 '07 #13
On Jun 19, 8:18 am, Jess <w...@hotmail.c omwrote:
Can a pure-function be non-virtual? I tried an example and my compiler
says no.
What would this mean? To me it would mean that all derived classes
must override this method, but since it is not virtual there is no
concept of overriding in the first place. This is why "pure" is tied
to "virtual". There is no pure without virtual, because only virtual
functions can be overriden.

I see, so I can declare a pure virtual function in a class and define
it later as in

class A{
virtual void f() = 0;

};

void A::f(){...}
Yes. This isn't very common but it's possible and useful sometimes.
If you find yourself doing this constantly, you may want to go back
and re-examine some of the design issues related to interfaces, pure
virtual functions, and abstract classes.

In addition, if I have a virtual destructor for an abstract class A,
then why do I still have to define it?
All classes must have a destructor defined. Just because you will
never create an object of type B doesn't mean you won't delete an
object through a B pointer.
Additionally, I don't think I can declare virtual constructor, but it
seems I can't make a pure constructor like
class C{
C() = 0;
virtual void f()=0;
};
Again, what would be the point? If you have a pure constructor but no
other pure virtual functions, then your class essentially does
nothing. If you have a pure virtual function but no pure constructor,
it works the same as if you did have a pure constructor.
Moreover, is there any function or operator that can't be virtual,
just like constructors?
Static member functions cannot be virtual. Since 'operator new'
and 'operator delete' are implicitly static, they cannot be virtual.

In that case, if I'd like a static member function to have polymorphic
behaviour, I think I need the static function to take an argument that
is a pointer to the base class object. Since this would allow the
static function to invoke an appropriate virtual function through the
pointer. Is this a correct approach?
static members can't be virtual because there is no "this" pointer.
there is no instance associated with a static function.

Jun 20 '07 #14
On Jun 20, 1:06 pm, Jess <w...@hotmail.c omwrote:
On Jun 20, 6:20 pm, James Kanze <james.ka...@gm ail.comwrote:
[...]
I think declaring a function as "=0" is the same
as not giving its definition, is this right?
Not at all. The rules are far more subtle. Declaring a
function pure virtual (i.e. appending "= 0" to its declaration)
has several effects:
1. it makes the class abstract,
2. it means that anytime dynamic lookup of the function
resolves to the function in this class, you have undefined
behavior, and
If a function is pure virtual but I still define it, would the
compiler put the function's address into vtbl?
Probably not, but if the compiled code ever has to look at that
entry in the vtbl, the program has undefined behavior, so
anything the compiler puts there is legal.
If the compiler puts the addr into the vtble, then I think it
wouldn't cause an undefined behaviour when this function is
called.
If the compiler documents that it puts the address there, then
they are defining undefined behavior. It's an extension. As I
said, most compilers put the address of a function which aborts
with an error message there. From a quality of implementation
point of view, that's probably the best solution.

Note that "undefined behavior", when used in this group,
normally refers to all behavior undefined by the language
specification, and doesn't take implementation specific
extensions into account. If the standard says that something is
undefined behavior, then it is undefined behavior regardless of
what the compiler does.
3. it means that you are not required to provide a definition
of the function unless it is explicitly called.
By "explicitly called", do you mean the example like "Base::f()"
below?
Yes. "Explicitly called" isn't really the correct expression, I
think. I should have said something like "called through a
qualified identifier", or something like that.
Concerning these points;
1. You cannot create an instance of an abstract class.
Attention, however. This does not mean that there are never
objects which have the dynamic type of the class. During
construction and destruction, the dynamic type evolves to
alway be that of the constructor or destructor. So if you
call a pure virtual function (directly or indirectly) while
executing the constructor or the destructor, the dynamic
resolution will result in the pure virtual function. Which
leads to point 2:
I don't quite understand this...What is "dynamic types evolve to be
that of constructor or destructor" and how can a pure virtual function
be called in a constructor or destructor?
The dynamic type of the object. Consider something like the
following:

class Base
{
public:
Base() ;
virtual void g() = 0 ;
} ;

class Middle : public Base
{
public:
Middle() ;
virtual void g()
{
std::cout << "Middle" << std::endl ;
}
} ;

class Derived : public Middle
{
public:
Derived() ;
virtual void g()
{
std::cout << "Derived" << std::endl ;
}
} ;

void
f( Base* p )
{
p->g() ;
}

Base::Base()
{
// f( this ) ; undefined behavior !!!
}

Middle::Middle( )
{
f( this ) ; // should call Middle::g()
}

Derived::Derive d()
{
f( this ) ; // should call Derived::g()
}

int
main()
{
Derived d ;
}

The function actually called in f depends on the dynamic type of
the object that p points to. As long as we are in the
constructor Middle::Middle( ), even if the final type of the
object will be Derived, the object behaves as if it were only a
Middle. If you uncomment the call to f() in Base::Base, then
the program has undefined behavior; I get the message
pure virtual method called
terminate called without an active exception
Aborted
from g++. (But the standard doesn't require this. Undefined
behavior is, well, undefined.) Adding a definition for
Base::g() doesn't change this. (But if I add the definition,
calling Base::g() is perfectly well behaved. And if I don't
calling Base::g() causes an error at link time---although as far
as the standard is concerned, it's undefined behavior.)
I had an experiment
#include<iostre am>
using namespace std;

class A{
public:
virtual ~A(){};
virtual void f()=0;
};
void A::f(){cout << "A::f()" << endl;}
class B:public A{
public:
int x;
B():x(10){f();}
void f(){cout << "B::f()" << endl;}
};
int main(){
B b;
return 0;
}
In the constructor B(), I called the virtual function f(). I remember
the book Effective C++ says if I call a virtual function (f()) in a
constructor (B()), then the function won't be resolved to B::f()
because the object is not yet of type B. Instead A::f() is called.
However, when I ran my program, I got B::f(). What happened?
I think you misunderstood something in Effective C++. When
calling f() in B::B(), it's exactly as if the type of the
complete object were B (which in this case it is). What will
cause problems is calling f() in A::A().
2. It's undefined behavior. From a quality of implementation
point of view, I would expect the program to abort, with an
error message, and this is, in fact, what most compilers do.
But it's not guaranteed, and you cannot count on it.
Literally anything can happen, and you must absolutely avoid
the case.
3. Normally, if a function is virtual, it is considered to be
used, regardless of whether you ever actually call it or
not. And if a function is used, it must defined, somewhere.
If a function is pure virtual, however, it is not
automatically considered used, and so a definition is only
required if you actually do use it; since it cannot be found
by dynamic lookup (point 2), the only way to use it is to
call it explicitly, i.e. Base::f().
So even if the pure virtual function is defined, its addr is
not put into vtbl?
Maybe. Anything you can do to find out whether it's there or
not is undefined behavior, so the compiler can do whatever it
wants. Most compilers will not put it in, since that could hide
an error.
Is the addr not stored at all, not in vtbl, not anywhere
else?
If you write Base::f(), then the address is stored somehow as
part of the call instruction. It's also likely stored in the
stack walkback tables for exception handling, and probably in
the debugging information. And anywhere else the compiler needs
it.
By the way, is this why my program above got B::f() instead of
A::f()? However, even if A::f() isn't in the vtbl, I thought the
object being constructed isn't of type B yet, so its B::f() shouldn't
be called.
Sort of:-). You're writing the constructor, and it is up to you
to know how far along you are in construction, and what
functions will or will not work. Most of the time, most
programmers try to do all critical initialization in the
constructor initializer list, so that once in the body of the
constructor, they have an object with consistent state, on which
they can usually call most functions. (Note the frequent use of
"most" and "usually" in the above. It's not an absolute rule,
and it must often be weighed against other considerations; I
frequently find myself violating it in templated constructors,
for example.)
Note that the compiler
will generate such calls to the same function in the base
class implicitly in constructors, the destructor, and in a
compiler generated assignment operator.
Do you mean if I have a derived class B, base class A, then in B's
constructor, the compiler generates A::A(), in B's destructor it
generates A::~A() etc?
Yes. It is, by definition, impossible to have an instance of A
without A's constructor having been called. Even if that
instance is only a base class subobject. Try my example above:
with the undefined behavior commented out, it prints:
Middle
Derived

I'm very surprised that you have to ask that. It's one of the
basics of C++, and should be covered by any book that explains
inheritance, no matter how simply.

--
James Kanze (GABI Software, from CAI) 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

Jun 20 '07 #15
On Jun 20, 1:12 pm, Jess <w...@hotmail.c omwrote:
On Jun 20, 6:30 pm, James Kanze <james.ka...@gm ail.comwrote:
In general, I've not heard the name pure abstract class, but
rather interface, for such classes. Probably an influence from
Java, which has a special keyword for such classes, and allows
multiple inheritance from them (but not from other classes). In
practice, of course, most real interfaces will want to define a
contract, which means that the public functions will be
non-virtual, forwarding to private (or protected) pure virtual
functions.
Why are public functions non-virtual?
Because public functions define the contract, and defining the
contract is the responsibility of the base class.
I thought an interface- implementation would look like
class A{
public:
virtual void f() = 0;
virtual void g() = 0;
};
class B:public A{
public:
void f();
void g();
};
void B::f(){}
void B::g(){}
In other words, A declares a set of virtual functions and B inherits
from A and implements the virtual functions.
That's one way of doing it. Now suppose that f() actually
returns an int in the range 1...6. How do you enforce this in
the above schema. Whereas:

class A
{
public:
int f() {
int result = doF() ;
assert( result >= 1 && result <= 6 ) ;
return result ;
}

private:
virtual int doF() = 0 ;
} ;

There's no way a derived class will accidentally violate the
contract.
Then a client program
can use A& or A* to do polymorphic operations.
The problem is what the client program can count on. If A
guarantees that A::f() will return a value in the range 1...6,
and the client code counts on it, then if the derived class
implements "int B::f() { return 10 ; }", the client will not be
able to use it. C++ doesn't provide any built-in mechanism for
detecting this sort of thing; the above schema does.
Is this not right? In
addition, when do we need to "forward to private (or protected) pure
virtual function"?
Whenever you have a contract that goes beyond what can be
expressed in a simple function signature. Typically, this is
the case most of the time. The major exception are design
patterns involving an inversion of the call sequence---callbacks
and such in everyday language.

--
James Kanze (GABI Software, from CAI) 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

Jun 20 '07 #16
On Jun 21, 2:39 am, James Kanze <james.ka...@gm ail.comwrote:
Sort of:-). You're writing the constructor, and it is up to you
to know how far along you are in construction, and what
functions will or will not work. Most of the time, most
programmers try to do all critical initialization in the
constructor initializer list, so that once in the body of the
constructor, they have an object with consistent state, on which
they can usually call most functions. (Note the frequent use of
"most" and "usually" in the above.
Thanks. :) I think in the constructor initializer list, we should
initialize the data members and probably call base class constructors.
What other "critical" initialization do we need to do there?
>
Note that the compiler
will generate such calls to the same function in the base
class implicitly in constructors, the destructor, and in a
compiler generated assignment operator.
Do you mean if I have a derived class B, base class A, then in B's
constructor, the compiler generates A::A(), in B's destructor it
generates A::~A() etc?

Yes. It is, by definition, impossible to have an instance of A
without A's constructor having been called. Even if that
instance is only a base class subobject. Try my example above:
with the undefined behavior commented out, it prints:
Middle
Derived

I'm very surprised that you have to ask that. It's one of the
basics of C++, and should be covered by any book that explains
inheritance, no matter how simply.
The books I read did say that. I was just not sure which "same
function" you refered to, so I probably asked a dumb question. :)
Jess

Jun 21 '07 #17
On Jun 21, 2:44 pm, Jess <w...@hotmail.c omwrote:
On Jun 21, 2:39 am, James Kanze <james.ka...@gm ail.comwrote:
Sort of:-). You're writing the constructor, and it is up to you
to know how far along you are in construction, and what
functions will or will not work. Most of the time, most
programmers try to do all critical initialization in the
constructor initializer list, so that once in the body of the
constructor, they have an object with consistent state, on which
they can usually call most functions. (Note the frequent use of
"most" and "usually" in the above.
Thanks. :) I think in the constructor initializer list, we should
initialize the data members and probably call base class constructors.
What other "critical" initialization do we need to do there?
Basically, you want to be sure that the class is in a state that
you can call member functions on it. This means that all base
classes and data members have been initialized. It shouldn't
mean anything more; at any rate, you can't do more than that in
the initializer list.

Note that the constructors for all base class and data members
*will* be called, whether they appear in the initializer list or
not. It's only important to put them in the initializer list
when the default constructor doesn't do what you want. (For
built-in types, of course, the default constructor doesn't do
anything, which is never what you want, so you'll always want to
initialize members with built-in type in the initializer list.)

Note to that initialization of the base classes and members
follows a pre-defined order, independantly of the order you
specify initialization in the initializer list. The order is:
1. Virtual base classes (only in the most derived class), in
the order they appear in a depth first left to right
traversal of the inheritance tree.
2. Immediate base classes, left to right.
3. Members, left to right.
(In the above, left to right means in the order the declarations
appear in the source code.) Some compilers will warn if the
initializers in the initialization list aren't in the same
order.
Note that the compiler
will generate such calls to the same function in the base
class implicitly in constructors, the destructor, and in a
compiler generated assignment operator.
Do you mean if I have a derived class B, base class A, then in B's
constructor, the compiler generates A::A(), in B's destructor it
generates A::~A() etc?
Yes. It is, by definition, impossible to have an instance of A
without A's constructor having been called. Even if that
instance is only a base class subobject. Try my example above:
with the undefined behavior commented out, it prints:
Middle
Derived
I'm very surprised that you have to ask that. It's one of the
basics of C++, and should be covered by any book that explains
inheritance, no matter how simply.
The books I read did say that. I was just not sure which "same
function" you refered to, so I probably asked a dumb question. :)
Not at all. I probably should have been clearer, since "same
function" isn't really very explicit here. What I meant was
simply that in the constructor, the compiler will generate calls
to the constructor, and in the destructor, calls to the
destructor. (But of course, A's constructor isn't the same
function as B's constructor, especially if they're both
overloaded.)

--
James Kanze (GABI Software, from CAI) 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

Jun 21 '07 #18

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

Similar topics

9
5030
by: richard.forrest1 | last post by:
I have a problem with an abstract interface class whose implementation classes need to return different iterator types (but with the same value_types etc). Classes A and B both conform to the same abstract Interface class. Interface has a pair of virtual functions begin() and end() that generate a typical STL style range. Classes A and B provide different implementations, maybe using different types of container. The problem is that, to...
62
3388
by: christopher diggins | last post by:
Since nobody responded to my earlier post , I thought I would try to explain what I am doing a bit differently. When multiply inheriting pure virtual (abstract) base classes, a class obviously bloats quickly for each new vtable needed. Execution slows down considerably as well. You can work around this by using interfaces referemnces which have a pointer to the object and a pointer to an external function lookup table. This technique...
10
1570
by: Britney | last post by:
1. what are virutal functions and how are they differenet from overloaded function? 2. what are interfaces and how are they different from abstract classes?
6
3137
by: Alden Pierre | last post by:
Hello, http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.7 As per the link above it's wise to have a virtual deconstructor when creating an abstract class. Here is when I'm little confused. Am I deleting the right object when I call the virtual deconstructor? I was under impression when creating a class if I'm not specifically allocating memory, do not implement deconstructor and let the default take care of it...
7
3090
by: eric | last post by:
hello i'm confused by an example in the book "Effective C++ Third Edition" and would be grateful for some help. here's the code: class Person { public: Person(); virtual ~Person(); // see item 7 for why this is virtual ...
14
4210
by: v4vijayakumar | last post by:
Why we need "virtual private member functions"? Why it is not an (compile time) error?
0
9255
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
10014
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...
1
9819
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
8688
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...
1
7226
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
6514
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
5119
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...
0
5289
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
2
3326
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.

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.