📄 c++.txt
字号:
delete[capacity] elements;
elements= new_array;
capacity= index+1;
}
return elements[index];
}
main()
{
asa a;
for (;;) {
char c;
int index;
char value[40];
cout << "operation (r,w,b,q)? ";
cin >> c;
switch (c) {
case 'q': goto done;
case 'b': // 'backwards' read test
cout << "enter string ";
cin >> value;
cout << "found at " << a[value];
break;
case 'r': //read test
cout << " index ";
cin >> index;
if (index < 0 || index >= a.size())
cout << "index out of range.";
else cout << "contains " << a[index];
break;
case 'w': //write test
cout << " index and value ";
cin >> index >> value;
a[index]= strdup(value);
break;
case 's': //size?
cout << "array holds " << a.size() << " elements.";
break;
case 'l': //list them
for (index= 0; index < a.size(); index++)
cout << "\n [" << index << "] " << a[index];
break;
}
cout << '\n';
}
done:
cout << "program finished.\n";
}
@END(LISTING)
This program was based heavily on the previous. I started with
the vector class. A global search and replace changed the name
from @I(vector) to @I(asa), and simply changing the typedef of eltype
changed the type to operate on char*'s instead of ints. Then I
added the new function: @I(int asa::operator[] (eltype s)). It
simply searches the array for a matching string.
The test driver also required only minor changes. Changing the
definition of the variable @I(value) to the new type also changed the
behavior of all the input and output statements that use it. All
I had to do was add a new case to try the string look up.
Note that this program is not all that great. The simple stream
input does not let me enter strings with a space in them, the
array is not initialized so listing it can cause problems, and it
is up to the user of the class to free up pointers before they
are overwritten.
@SECTION(operator())
The operator() is another strange one. It is sometimes called
the @I(function-call operator). Like operator[], it must be a
member. The way to call it is easier shown than explained. Look
how it is used:
@BEGIN(LISTING)
class C {
//stuff...
public:
void operator() (int x);
};
C x;
//call operator()
x(5);
x.operator()(5); //same thing
@END(LISTING)
Here, @I(x) is a variable of type @I(C). Using the name as if it were a
function will call operator(). When defining operator(), you
give a parameter list just line any other operator, so you have
two sets of parenthesis in the definition. When calling it, you
just have the one set. The operator() can be defined to have any
number of arguments-- it is the only operator that can do this.
@BEGIN(LISTING)
void C::operator()(); //no args
int C::operator() (char* s, int y); //2 args
@END(LISTING)
So what is the point in having such an operator? For one, it can
be used in a manner similar to the operator[]. Here is a vector
class that uses operator[] to access an element with range
checking, and operator() to access an element without range
checking.
@BEGIN(LISTING)
// example of using operator() on a vector similar to operator[]
typedef int eltype;
class vector {
eltype* contents;
int first, last; //bounds of the array
public:
vector (int first, int last);
~vector() { delete[last-first+1] contents; }
eltype& operator[] (int index); //access with range checking
eltype& operator() (int index) //access without range
checking
{ return contents[index-first]; }
};
vector::vector (int f, int l)
{
first= f;
last= l;
contents= new eltype[l-f+1];
}
eltype& vector::operator[] (int index)
{
if (index < first || index > last) {
//deal with the error somehow. In this case,
//I'll return a special internal value that can
//be written to without trashing memory.
static eltype dump;
return dump;
}
return contents[index-first];
}
@END(LISTING)
If @I(a) is of type vector, you could access @I(a[5]) or @I(a(5)) which are
similar in meaning. Having both [] and () available gives two
different ways to subscript a vector.
Another thing you can do with the operator() is to take advantage
of its ability to have different numbers of arguments. The
operator[] can only take one argument, but you might have a
matrix class that takes two subscripts. You could use operator()
to subscript the class instead.
@BEGIN(LISTING)
// example of using operator() instead of operator[]
// so I can use two arguments.
#include <assert.h>
const int matsize= 3;
typedef double eltype;
class square {
eltype data[matsize][matsize];
public:
eltype& operator() (int x, int y);
};
eltype& square::operator() (int x, int y)
{
assert (x > 0 && y > 0 && x < matsize && y < matsize);
return data[x][y];
}
@END(LISTING)
Given a variable @I(M) of type @I(square), you could write @I(M(1,2)) to
access an element.
In the class definition, the eltype definition is used as usual.
In this example, the size of the matrix is specified with @I(matsize)
as well. To change the size, you only need to change this
definition. All other parts of the code reference the size by
this name. In C, you would have to use a #define for this
purpose. In C++, a const variable can be used in constant
expressions, such as the size of an array definition.
Another variation on this theme is a string class which uses
operator() to take a substring. Writing @I(s(3,7)) would return the
third through seventh characters in the string s.
Another time operator() is used in when a class only has one
method, or one very important method. Consider an iterator
class. It steps through a linked list, returning the next
element each time the @I(next()) method is called. Rather than
calling it @I(next()), this sometimes uses operator() for that
purpose.
@BEGIN(LISTING)
// example of using operator() in an iterator
class node {
friend class iterator; //grant class iterator access to my
private data
node* next;
public:
char* data;
void insert_after (node*); //insert a node after this node.
};
class iterator {
node* p; //keep track of my position in the list
public:
node* operator()(); //advance to the next position
iterator (node* n) { p= n; }
};
void node::insert_after (node* p)
{ //insert p after this node
p->next= next;
next= p;
}
node* iterator::operator()()
{ //return the node and advance to the next node
if (!p) return p; //end of the line, don't advance
node* temp= p;
p= p->next;
return temp;
}
@END(LISTING)
@SECTION(operator->)
The operator-> is the strangest one of all. It must be a member.
How it is called, and what it does, takes some explaining.
The operator-> must be defined to return a pointer type or a
class type that itself has an operator-> defined. The call is
made with an object on the left and a member name on the right,
such as @I(x->a), but the @I(a) is not an argument to the function. The
operator-> is applied to @I(x), and then the result is used on the
left side of -> again. The @I(a) will be the name of a field, not an
argument of any kind.
The example @I(x->a) is equivalent to @I((x.operator->())->a). The
operator is "slipped in" to the member access.
Here is an example of using operator-> to implement a "smart
pointer".
@BEGIN(LISTING)
//example of using operator-> to create a "smart pointer"
class C {
//members go here...
public:
int x; //a public data member
void dosomething(); //member function
};
class Cptr {
C* p;
public:
Cptr() { p=0; } //always initialized
Cptr& operator= (C* ptr) { p= ptr; return *this; };
C* operator->();
};
extern void error(); //report an error, somehow
C* Cptr::operator->()
{
if (!p) { //oops! NULL pointer
error();
}
else return p;
}
@END(LISTING)
The smart pointer class holds a pointer to a C, and has
operator-> defined on it so it will return that pointer. You
could have:
@BEGIN(LISTING)
C x;
Cptr p;
p= &x;
y= p->x; //refer to element in C
p->dosomething(); //even member functions
@END(LISTING)
The operator-> is a unary operator that returns a C*, and then
the -> operation is redone with that return value on the left.
So @I(p->x) is equivalent to @I((p.operator->())->x;). If that still
confuses you, remember that the operator is simply a member
function with a funny name. Calling it @I(fetch()) would let you say
@I((p.fetch())->x;) which is perhaps clearer.
@SECTION(operator&)
The unary form operator& is unusual because it is already defined
for all class type. It normally takes the address of the object.
You can redefine it to do anything you want. Normally it is used
to inform the system that an object is having its address taken.
It is sometimes used in debug code to report to the programmer
when objects have their address taken.
@BEGIN(LISTING)
someclass* someclass::operator& ()
{
report_on (this); //tell the programmer
return this; //and do what I came for.
}
@END(LISTING)
This operator can be defined as a member function as shown above,
or as a non-member function. You should watch out for pitfalls
when using an operator&. Namely, how do you take the address of
an object if operator& is defined? This suggests one use of
operator& is to make a class where you cannot take the address of
an object-- operator& is private or causes a run-time error to be
printed.
In the example of operator-> a smart pointer was illustrated.
The Cptr type had an operator= that let you assign a C* to a
Cptr. Instead, you could use operator& to make you use smart
pointers exclusively: define a @I(Cptr C::operator&();) so that
taking the address of a C object gives a smart pointer directly.
@SECTION(operator,)
The comma operator is not all that unusual. It is rarely used
because the comma is used so much in C++ already. It is unusual
in the respect that the comma is already defined between class
types, so you have to be careful sometimes in knowing how
overloading with resolve.
The comma operator forces left-to-right evaluation. That can be
handy. The order of precedence is very low, so you will usually
need parenthesis around the comma expression.
@b(come up with an example)
@SECTION(operator++ and operator--)
These two are similar, and I'll just talk about operator++. The
same comments apply to operator--.
There are two forms of ++ for built-in types. As a C programmer,
you know that @I(y= x++;) and @I(y= ++x); has different meanings. In C++
you can define both the prefix and postfix forms.
Defining the prefix form is exactly what you would expect, since
it is the same as any other unary operator. A member function
such as @I(C::operator()) or a nonmember such as @I(operator++(C&))
will do.
The postfix form is defined with an extra argument. This second
argument is an int, and is always passed a value of 0. Defining
@I(C::operator++(int)) or @I(operator++ (C&,int)) will define a postfix
operator.
Here is the smart pointer example again, showing prefix and
postfix operators added.
@BEGIN(LISTING)
//example of using operator++ with the smart pointers
class C {
//members go here...
public:
int x; //a public data member
void dosomething(); //member function
};
class Cptr {
C* p;
public:
Cptr() { p=0; } //always initialized
Cptr& operator= (C* ptr) { p= ptr; return *this; };
C* operator->(); //see example in operator-> section
Cptr& operator++(); //preincrement
Cptr operator++(int); //postincrement
};
Cptr& Cptr::operator++()
{ //preincrement
++p;
return *this;
}
Cptr operator++ (int)
{ //postincrement
Cptr temp= *this;
++p;
return temp; //return original unmodified copy
}
@END(LISTING)
@b(Version Note)
In C++ versions prior to 2.1, the postfix form was not available.
The only way to define an operator++ or operator-- was with one
argument. It called this same function for either prefix or
postfix use. For compatibility, do not use such a function as a
postfix operator.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -