3.Writing Larger Programs

要想写一个稍微大一点项目,以下知识需要继续巩固一下,再次回到基础知识哈。

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();
}

如果计划构建一个更大的程序,那么此时最好将类定义和函数声明放在一个单独的文件中。正如我们之前讨论的头文件一样,将类定义放在单独的头中有助于组织代码,并防止在定义类之前尝试使用类对象出现问题。

  1. 当类方法在类之外定义时,必须使用scope resolution操作符::来指示该方法属于哪个类。例如,在PrintCarData方法的定义中可以看到:
void Car::PrintCarData()

如果有两个类的方法具有相同的名称,这可以防止任何编译器问题。

  1. 改变了构造函数初始化变量的方式。而不是以前的构造函数:
Car(string c, int n) {
   color = c; 
   number = n;
}

构造函数现在使用初始值列表:

Car(string c, int n) : color(c), number(n) {}

类成员在构造函数的主体(现在是空的)之前初始化。初始化器列表是在构造函数中初始化许多类属性的快速方法。此外,编译器处理列表中初始化的属性与在构造函数体中初始化的属性略有不同。如果类属性是引用,则必须使用初始值设定项列表对其进行初始化。

  1. 不需要在类外部可见的变量设置为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();
  1. 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,接下来将会更加有趣。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容