要想写一个稍微大一点项目,以下知识需要继续巩固一下,再次回到基础知识哈。
1. 头文件
头文件或.h文件允许将相关的函数,方法和类声明收集在一个位置。然后可以将相应的定义放置在.cpp文件中。编译器认为头声明是一个“承诺”,该定义将在后面代码找到。因此,如果编译器尚未找到定义的函数,则可以继续进行编译,直到找到定义为止。这允许以任意顺序定义(和声明)函数。
2. 引用
#include <iostream>
using std::cout;
int main()
{
int i = 1;
// Declare a reference to i.
int& j = i;
cout << "The value of j is: " << j << "\n";
// Change the value of i.
i = 5;
cout << "The value of i is changed to: " << i << "\n";
cout << "The value of j is now: " << j << "\n";
// Change the value of the reference.
// Since reference is just another name for the variable,
// th
j = 7;
cout << "The value of j is now: " << j << "\n";
cout << "The value of i is changed to: " << i << "\n";
}
3.指针
C ++指针只是一个变量,用于存储程序中对象的内存地址。
#include <iostream>
using std::cout;
int main() {
int i = 5;
int j = 6;
// Print the memory addresses of i and j
cout << "The address of i is: " << &i << "\n";
cout << "The address of j is: " << &j << "\n";
}
可能想知道为什么同一个符号既可以用来访问内存地址,也可以像以前看到的那样,将引用传递到函数中:
1.符号&和*有不同的含义,这取决于它们出现在等式的哪一边。
2.记住这一点非常重要。对于&,如果它出现在等式的左侧(例如,声明变量时),则表示该变量声明为引用。如果&出现在等式的右侧,或在先前定义的变量之前,则用于返回内存地址,如上面的示例所示。
#include <iostream>
using std::cout;
int main()
{
int i = 5;
// A pointer pointer_to_i is declared and initialized to the address of i.
int* pointer_to_i = &i;
// Print the memory addresses of i and j
cout << "The address of i is: " << &i << "\n";
cout << "The variable pointer_to_i is: " << pointer_to_i << "\n";
}
从代码中可以看到,变量pointer_to_i使用*符号声明为指向int的指针,pointer_to_i设置为i的地址。从打印输出中可以看出,pointer_to_i的值与i的地址相同。
一旦有了指针,您可能需要检索它所指向的对象。在这种情况下,*符号可以再次使用。然而,这一次,它将出现在一个方程的右边或一个已经定义的变量的前面,所以含义是不同的。在这种情况下,它被称为“解引用运算符”,并返回被指向的对象。您可以在下面的代码中看到这是如何工作的:
#include <iostream>
using std::cout;
int main()
{
int i = 5;
// A pointer pointer_to_i is declared and initialized to the address of i.
int* pointer_to_i = &i;
// Print the memory addresses of i and j
cout << "The address of i is: " << &i << "\n";
cout << "The variable pointer_to_i is: " << pointer_to_i << "\n";
cout << "The value of the variable pointed to by pointer_to_i is: " << *pointer_to_i << "\n";
}
4.引用与指针
指针和引用可以在C++中使用类似的用例。如前所述,引用和指针都可以在对函数的按次传递引用中使用。此外,它们都提供了一种访问现有变量的可选方法:指针通过变量的地址,以及通过该变量的另一个名称引用。但是两者有什么区别,什么时候应该使用它们呢?下面的列表总结了指针和引用之间的一些差异,以及应该在何时使用它们:
引用:
引用在声明时必须初始化。这意味着引用将始终指向有意分配给它的数据。
指针:
指针可以在不初始化的情况下声明。如果错误地发生这种情况,指针可能指向内存中的任意地址,与该地址关联的数据可能毫无意义,从而导致未定义的行为和难以解决的错误。
引用:
引用不能为空。这意味着引用应该指向程序中有意义的数据。
指针:
指针可以为空。事实上,如果指针没有立即初始化,通常最好将其初始化为nullptr,这是一种表示指针为空的特殊类型。
引用:
当在用于按引用传递的函数中使用时,该引用可以作为相同类型的变量使用。
指针:
在用于按引用传递的函数中使用时,必须取消对指针的引用才能访问基础对象。
引用通常比指针更简单和安全。作为一个很好的经验法则,在可能的情况下,应该使用引用来代替指针。
但是,有时不能使用引用。一个例子是对象初始化。您可能希望一个对象存储对另一个对象的引用。
但是,如果在创建第一个对象时另一个对象尚不可用,则第一个对象将需要使用指针,而不是引用,因为引用不能为空,只能在创建另一个对象后初始化引用。
5.
#include <iostream>
#include <string>
using std::string;
using std::cout;
int main()
{
// Variables to hold each car's color.
string car_1_color = "green";
string car_2_color = "red";
string car_3_color = "blue";
// Variables to hold each car's initial position.
int car_1_distance = 0;
int car_2_distance = 0;
int car_3_distance = 0;
// Increment car_1's position by 1.
car_1_distance++;
// Print out the position and color of each car.
cout << "The distance that the " << car_1_color << " car 1 has traveled is: " << car_1_distance << "\n";
cout << "The distance that the " << car_2_color << " car 2 has traveled is: " << car_2_distance << "\n";
cout << "The distance that the " << car_3_color << " car 3 has traveled is: " << car_3_distance << "\n";
}
这适用于程序中定义的少数汽车,但是如果您希望程序跟踪许多汽车,这将非常麻烦。你需要为每辆车创建一个新的变量,代码会很快变得混乱。解决这个问题的一种方法是定义一个Car类,将这些变量作为属性,同时使用一些类方法来增加行驶距离并打印出汽车数据。
#include <iostream>
#include <string>
using std::string;
using std::cout;
// The Car class
class Car {
public:
// Method to print data.
void PrintCarData()
{
cout << "The distance that the " << color << " car " << number << " has traveled is: " << distance << "\n";
}
// Method to increment the distance travelled.
void IncrementDistance()
{
distance++;
}
// Class/object attributes
string color;
int distance = 0;
int number;
};
int main()
{
// Create class instances for each car.
Car car_1, car_2, car_3;
// Set each instance's color.
car_1.color = "green";
car_2.color = "red";
car_3.color = "blue";
// Set each instance's number.
car_1.number = 1;
car_2.number = 2;
car_3.number = 3;
// Increment car_1's position by 1.
car_1.IncrementDistance();
// Print out the position and color of each car.
car_1.PrintCarData();
car_2.PrintCarData();
car_3.PrintCarData();
}
这看起来没问题,而且已经减少了main中的变量数量,所以可能会看到在未来如何更有条理。然而,现在的代码比开始时多得多,而且main似乎没有组织得多。上面的代码仍然在创建汽车之后为每辆汽车设置属性。
添加一个构造函数
解决这个问题的最好方法是向Car类添加构造函数。构造函数允许您使用所需的数据实例化新对象。在下一个代码单元中,我们为Car添加了一个构造函数,它允许传入数字和颜色。这意味着可以用这些变量创建每个Car对象。
#include <iostream>
#include <string>
using std::string;
using std::cout;
class Car {
public:
void PrintCarData()
{
cout << "The distance that the " << color << " car " << number << " has traveled is: " << distance << "\n";
}
void IncrementDistance()
{
distance++;
}
// Adding a constructor here:
Car(string c, int n)
{
// Setting the class attributes with
// The values passed into the constructor.
color = c;
number = n;
}
string color;
int distance = 0;
int number;
};
int main()
{
// Create class instances for each car.
Car car_1 = Car("green", 1);
Car car_2 = Car("red", 2);
Car car_3 = Car("blue", 3);
// Increment car_1's position by 1.
car_1.IncrementDistance();
// Print out the position and color of each car.
car_1.PrintCarData();
car_2.PrintCarData();
car_3.PrintCarData();
}
如果计划构建一个更大的程序,那么此时最好将类定义和函数声明放在一个单独的文件中。正如我们之前讨论的头文件一样,将类定义放在单独的头中有助于组织代码,并防止在定义类之前尝试使用类对象出现问题。
- 当类方法在类之外定义时,必须使用scope resolution操作符::来指示该方法属于哪个类。例如,在PrintCarData方法的定义中可以看到:
void Car::PrintCarData()
如果有两个类的方法具有相同的名称,这可以防止任何编译器问题。
- 改变了构造函数初始化变量的方式。而不是以前的构造函数:
Car(string c, int n) {
color = c;
number = n;
}
构造函数现在使用初始值列表:
Car(string c, int n) : color(c), number(n) {}
类成员在构造函数的主体(现在是空的)之前初始化。初始化器列表是在构造函数中初始化许多类属性的快速方法。此外,编译器处理列表中初始化的属性与在构造函数体中初始化的属性略有不同。如果类属性是引用,则必须使用初始值设定项列表对其进行初始化。
- 不需要在类外部可见的变量设置为private。这意味着不能在类之外访问它们,这可以防止它们被意外更改。
声明位于.h文件中,定义位于.cpp中
#ifndef CAR_H
#define CAR_H
#include <string>
using std::string;
using std::cout;
class Car {
public:
void PrintCarData();
void IncrementDistance();
// Using a constructor list in the constructor:
Car(string c, int n) : color(c), number(n) {}
// The variables do not need to be accessed outside of
// functions from this class, so we can set them to private.
private:
string color;
int distance = 0;
int number;
};
#endif
#include <iostream>
#include "car.h"
// Method definitions for the Car class.
void Car::PrintCarData()
{
cout << "The distance that the " << color << " car " << number << " has traveled is: " << distance << "\n";
}
void Car::IncrementDistance()
{
distance++;
}
car_main.cpp
#include <iostream>
#include <string>
#include "car.h"
using std::string;
using std::cout;
int main()
{
// Create class instances for each car.
Car car_1 = Car("green", 1);
Car car_2 = Car("red", 2);
Car car_3 = Car("blue", 3);
// Increment car_1's position by 1.
car_1.IncrementDistance();
// Print out the position and color of each car.
car_1.PrintCarData();
car_2.PrintCarData();
car_3.PrintCarData();
}
6.继续扩大规模
1.在下面的代码中,cp是指向Car对象的指针,下面两个是等价的:
cp->IncrementDistance();
// Dereference the pointer using *, then
// access IncrementDistance() with traditional
// dot notation.
(*cp).IncrementDistance();
- new在“堆”上为对象分配内存。通常,必须手动管理(释放)此内存,以避免程序中出现内存泄漏。这很危险,因此通常最好在“stack”上创建对象,而不使用“new”操作符。
#include <iostream>
#include <string>
#include <vector>
#include "car.h"
using std::string;
using std::cout;
using std::vector;
int main() {
// Create an empty vector of pointers to Cars
// and a null pointer to a car.
vector<Car*> car_vect;
Car* cp = nullptr;
// The vector of colors for the cars:
vector<string> colors {"red", "blue", "green"};
// Create 100 cars with different colors and
// push pointers to each of those cars into the vector.
for (int i = 0; i < 100; i++) {;
cp = new Car(colors[i % 3], i + 1);
car_vect.push_back(cp);
}
// Move each car forward by 1.
for (Car* cp: car_vect) {
cp->IncrementDistance();
}
// Print data about each car.
for (Car* cp: car_vect) {
cp->PrintCarData();
}
}
ok,接下来将会更加有趣。