这篇文章继续总结C++的基础知识,这里不对每个知识做详细介绍,而是尽量从代码中理解这些特性。
1 模板
1.1 函数模板
函数模板就是可以使用泛型来定义函数,它允许将类型作为参数传递给模板,然后编译器就会生成该类型的函数。比如我们想实现一个交换两个数的函数,但是这两个数有可能是int
类型,也可能是double
类型,我们就可以这样来实现:
template <typename AnyType>
void Swap(AnyType &a, AnyType &b)
{
AnyType temp;
temp = a;
a = b;
b = temp;
}
上面的代码中建立了一个类型名为AnyType的模板,这样如果我们传了两个double
给Swap
函数,编译器就会自动生成对应的参数类型为double
的Swap
函数。
在C++98添加关键字typename
之前,C++使用class
关键字来创建模板,也就是说template <typename AnyType>
也可以声明为template <class AnyType>
1.2 类模板
对于一个class中的变量类型,也可以使用类模板来指定。下面来看一个Stack
类,用户可以指定栈中元素的类型:
template <class T>
class Stack
{
public:
Stack(int = 10) ;
~Stack() { delete [] stackPtr ; }
int push(const T&);
int pop(T&) ;
int isEmpty()const { return top == -1 ; }
int isFull() const { return top == size - 1 ; }
private:
int size ; // number of elements on Stack.
int top ;
T* stackPtr ;
} ;
注意:类模板成员函数的声明和定义需要放在同一个头文件中。以stack.h
为例:
//stack.h
#pragma once
template <class T>
class Stack
{
public:
Stack(int = 10) ;
~Stack() { delete [] stackPtr ; }
int push(const T&);
int pop(T&) ; // pop an element off the stack
int isEmpty()const { return top == -1 ; }
int isFull() const { return top == size - 1 ; }
private:
int size ; // Number of elements on Stack
int top ;
T* stackPtr ;
} ;
//constructor with the default size 10
template <class T>
Stack<T>::Stack(int s)
{
size = s > 0 && s < 1000 ? s : 10 ;
top = -1 ; // initialize stack
stackPtr = new T[size] ;
}
// push an element onto the Stack
template <class T>
int Stack<T>::push(const T& item)
{
if (!isFull())
{
stackPtr[++top] = item ;
return 1 ; // push successful
}
return 0 ; // push unsuccessful
}
// pop an element off the Stack
template <class T>
int Stack<T>::pop(T& popValue)
{
if (!isEmpty())
{
popValue = stackPtr[top--] ;
return 1 ; // pop successful
}
return 0 ; // pop unsuccessful
}
使用例子:
#include <iostream>
#include "stack.h"
using namespace std ;
void main()
{
typedef Stack<float> FloatStack ;
typedef Stack<int> IntStack ;
FloatStack fs(5) ;
float f = 1.1 ;
cout << "Pushing elements onto fs" << endl ;
while (fs.push(f))
{
cout << f << ' ' ;
f += 1.1 ;
}
cout << endl << "Stack Full." << endl
<< endl << "Popping elements from fs" << endl ;
while (fs.pop(f))
cout << f << ' ' ;
cout << endl << "Stack Empty" << endl ;
cout << endl ;
IntStack is ;
int i = 1.1 ;
cout << "Pushing elements onto is" << endl ;
while (is.push(i))
{
cout << i << ' ' ;
i += 1 ;
}
cout << endl << "Stack Full" << endl
<< endl << "Popping elements from is" << endl ;
while (is.pop(i))
cout << i << ' ' ;
cout << endl << "Stack Empty" << endl ;
}
--------------------------输出----------------------------------
Pushing elements onto fs
1.1 2.2 3.3 4.4 5.5
Stack Full.
Popping elements from fs
5.5 4.4 3.3 2.2 1.1
Stack Empty
Pushing elements onto is
1 2 3 4 5 6 7 8 9 10
Stack Full
Popping elements from is
10 9 8 7 6 5 4 3 2 1
Stack Empty
2 内联函数
在类定义中实现的函数将变为内联函数,下面的代码中set_tot
为内联函数。
class Stock
{
private:
long shares;
double share_val;
double total_val;
void set_tot() {total_val = shares * share_val;}
}
当然我们也可以在外部内联:
class Stock
{
...
void set_tot();
...
}
inline void Stock::set_tot()
{
total_val = shares * share_val;
}
3 const类和函数
假设一个类你要声明为const
,则该对象下的函数也应该确保不修改类中的变量。
const Stock land;
land.show();
----
其中show函数应该这样声明:
void show() const;
定义:
void stock::show() const;
而在函数后面加上const关键字可以指定该函数为常量成员函数。常量成员函数表示在该函数中,不能修改类的成员变量(mutable
成员变量除外),以确保函数不会对对象的状态造成修改。
4 运算符重载
C++中的运算符重载是指通过定义类成员函数或全局函数来重新定义已有的运算符的行为。运算符重载使得类对象可以像内置类型一样使用运算符,从而增加了代码的可读性和灵活性。下面看两个例子:
(1)减法运算符重载
class Vector {
public:
int x;
int y;
Vector operator-(const Vector& other) const {
Vector result;
result.x = this->x - other.x;
result.y = this->y - other.y;
return result;
}
};
int main() {
Vector v1{5, 6};
Vector v2{2, 3};
Vector diff = v1 - v2;
// diff.x = 3, diff.y = 3
return 0;
}
(2)乘法运算符重载
class Vector {
public:
int x;
int y;
Vector operator*(int scalar) const {
Vector result;
result.x = this->x * scalar;
result.y = this->y * scalar;
return result;
}
};
int main() {
Vector v{2, 3};
int scalar = 5;
Vector scaled = v * scalar;
// scaled.x = 10, scaled.y = 15
return 0;
}
5 友元
在C++中,友元是一种机制,允许非成员函数或非成员类访问类的私有成员。通过将函数或类声明为友元,可以使得这些外部实体在需要时获得对类的私有成员的访问权限。
友元提供了一种特殊的访问权限,但它并不是封装原则的一部分,应慎重使用。
- 友元函数:是声明为友元的非成员函数。通过在类中声明该函数为友元,可以使该函数访问类的私有成员变量和私有成员函数。
- 友元类:是另一个类声明为另一个类的友元。这意味着友元类可以访问被声明为友元的类的私有成员。
下面看一个例子:
#include <iostream>
class MyClass {
private:
int privateData;
friend void friendFunction(const MyClass& obj);
friend class FriendClass;
public:
MyClass(int data) : privateData(data) {}
};
void friendFunction(const MyClass& obj) {
int data = obj.privateData; // 访问私有成员变量
std::cout << "Friend Function: " << data << std::endl;
}
class FriendClass {
public:
void accessPrivateMember(const MyClass& obj) {
int data = obj.privateData; // 访问私有成员变量
std::cout << "Friend Class: " << data << std::endl;
}
};
int main() {
MyClass obj(42);
friendFunction(obj);
FriendClass friendObj;
friendObj.accessPrivateMember(obj);
return 0;
}
在这个示例中,定义了一个名为MyClass
的类,它有一个私有成员变量privateData
。friendFunction
函数被声明为MyClass
的友元函数,它是一个全局函数,且可以访问privateData
。同样地,FriendClass
类也被声明为MyClass
的友元类,可以访问privateData
。
在main函数中,创建了一个MyClass
对象obj,然后调用friendFunction
函数和FriendClass
类的accessPrivateMember
函数,表示友元函数和友元类对私有成员有访问权限。
6 总结
本篇文章对C++中的模板、内联函数、const类和函数、运算符重载和友元做了一个简单地介绍,并实现了相关的代码例子,为的是知道有这些概念,并能简单地使用它们。对于深入的用法,这里不做讲解。