c++ day 2
昨天在后面又复习了关于c++类的相关知识
这段建议好好看书
下面是实现了一个简单的类
1 class currency 2 { 3 public : 4 // 构造函数 5 currency (signType theSign = plus, unsigned long theDollars = 0, unsigned int theCents = 0); 6 // 析构函数 7 ~currency() {} 8 void setValue(signType, unsigned long, unsigned int); 9 void setValue(double); 10 signType getSign() const {return sign;} 11 unsigned long getDollars() const {return dollars;} 12 unsigned int getCents() const {return cents;} 13 currency add(const currency&) const; 14 currency& increment(const currency&); 15 void output() const; 16 private : 17 signType sign; // 符号 18 unsigned long dollars; // 美元 19 unsigned int cents; // 美分 20 21 };
构造函数是一种特殊的成员函数,用于在创建对象时进行初始化操作。它的主要目的是确保对象在创建后处于有效的状态,并完成必要的设置和初始化工作。
构造函数的特点和用法如下:
-
函数名称与类名相同:构造函数的名称与所在类的名称完全相同,包括大小写。
-
没有返回类型:与普通成员函数不同,构造函数没有返回类型,包括void。这是因为构造函数的主要任务是初始化对象,而不是返回某个特定的值。
-
可以有参数:构造函数可以带有参数,用于接受初始化对象所需的数据。这些参数可以在对象创建时传递给构造函数,以便进行初始化操作。
-
自动调用:当创建一个对象时(使用new运算符或定义一个局部对象),构造函数会自动调用。它会在对象的内存空间分配完成后立即执行。
-
可以重载:类可以定义多个构造函数,每个构造函数可以有不同的参数列表。这被称为构造函数的重载。根据传递给构造函数的参数类型和数量的不同,编译器会选择调用相应的构造函数。
-
默认构造函数:如果没有显式定义任何构造函数,编译器会生成一个默认构造函数。默认构造函数不带任何参数,并执行默认的初始化操作。如果自定义了构造函数,编译器将不会生成默认构造函数。
-
成员初始化列表:构造函数可以使用成员初始化列表来对对象的数据成员进行初始化。成员初始化列表位于构造函数的定义之后,使用冒号(:)分隔。
构造函数在创建对象时扮演着重要的角色,它们可以执行各种初始化操作,例如分配动态内存、设置默认值、打开文件等。通过定义适当的构造函数,可以确保对象在创建后处于正确的状态,减少错误和异常的发生。
currency::currency(signType theSign, unsigned long theDollars, unsigned int theCents) { sign = theSign; dollars = theDollars; cents = theCents; }
这个构造函数接受三个参数:theSign
、theDollars
和theCents
,用于初始化Currency对象的数据成员。
在构造函数的函数体内,将传入的参数分别赋值给类的私有数据成员。theSign
被赋值给sign
,theDollars
被赋值给dollars
,theCents
被赋值给cents
。
通过这个构造函数,你可以在创建Currency对象时指定符号、美元和美分的初始值。如果不提供参数,默认情况下符号为正号,美元和美分的值为0。
需要注意的是,在构造函数的定义中没有明确指定Currency类的作用域,假设你在类的定义中添加了currency::
作为类的限定符,以使其位于类的命名空间中。
这个构造函数可以用于创建Currency对象并初始化其数据成员,例如:
currency c1(plus, 10, 50); // 创建一个带正号、10美元50美分的Currency对象 currency c2; // 创建一个默认对象,符号为正号,美元和美分的值为0
使用构造函数,你可以灵活地创建Currency对象并为其设置初始值。
Currency类的析构函数如下:
currency::~currency() {}
这是一个空的析构函数,没有任何具体的实现代码。空的析构函数表示在对象被销毁时不需要执行任何特殊的清理操作。
析构函数是在对象生命周期结束时自动调用的特殊成员函数。在Currency类中,析构函数被用于在对象销毁时进行清理和释放资源的操作。
由于该析构函数是空的,它不会执行任何清理操作,也不会释放任何资源。这意味着Currency对象在销毁时不需要进行额外的操作,没有动态分配的内存或打开的文件需要处理。
Currency类中的getSign()
、getDollars()
和getCents()
函数被声明为常量函数。常量函数是指在函数声明和定义中使用const
关键字修饰的成员函数。
常量函数的特点如下:
-
不修改对象的状态:常量函数承诺不会修改调用它的对象的状态。它们保证在函数体内部不会对对象的数据成员进行修改。这是为了防止通过常量函数修改对象的状态,从而提供了对象状态的只读访问。
-
可以读取对象的数据成员:常量函数可以读取对象的数据成员,包括私有成员。它们提供了对对象数据成员的只读访问。
-
适用于常量对象和非常量对象:常量函数可以在常量对象和非常量对象上调用。对于常量对象,常量函数提供了唯一可用的访问途径。
getSign()
、getDollars()
和getCents()
函数都被声明为常量函数。这意味着这些函数可以在常量对象上调用,并允许读取对象的sign
、dollars
和cents
数据成员的值,而不会修改对象的状态。
例如,对于常量对象 const currency c;
,你可以这样调用常量函数:
1 signType s = c.getSign(); // 读取常量对象 c 的符号 2 unsigned long d = c.getDollars(); // 读取常量对象 c 的美元值 3 unsigned int cents = c.getCents(); // 读取常量对象 c 的美分值
使用常量函数可以确保对对象进行只读访问,并提供对对象数据成员的安全访问。
我的currency类并没有写出复制构造函数 下面是一个复制构造函数的示例:
1 currency::currency(const currency& other) 2 { 3 sign = other.sign; 4 dollars = other.dollars; 5 cents = other.cents; 6 }
复制构造函数用于创建一个新对象,该对象的数据成员与另一个同类型对象(other
)相同。在你的Currency类中,复制构造函数被定义为接受一个常量引用参数,用于表示要复制的对象。
在复制构造函数的函数体中,将other
对象的数据成员分别赋值给新对象的对应数据成员。这样,新对象将拥有与原始对象相同的数据。
通过复制构造函数,可以实现对象的深层复制。这意味着新对象将拥有与原始对象相同的数据值,而不仅仅是简单地将指针或引用复制给新对象。
使用复制构造函数可以在创建对象时以另一个对象为模板进行初始化,例如:
1 currency c1(plus, 10, 50); // 创建一个原始对象 c1 2 currency c2(c1); // 使用复制构造函数创建一个新对象 c2,其数据与 c1 相同
在上述代码中,c2
对象通过复制构造函数使用c1
对象进行初始化,从而创建一个新的Currency对象,并拥有与c1
相同的符号、美元和美分值。
需要注意的是,如果没有显式定义复制构造函数,编译器会为类生成一个默认的复制构造函数。默认的复制构造函数执行成员逐个拷贝,对于大多数情况来说是合适的。如果类中包含指针成员或需要深度复制的资源,你可能需要自定义复制构造函数来实现适当的复制操作。
下面是给私有数据成员赋值的函数:
1 void currency::setValue(signType theSign, unsigned long theDollars, unsigned int theCents) 2 { 3 if (theCents > 99) 4 { 5 throw "Cents should be < 100"; 6 } 7 sign = theSign; 8 dollars = theDollars; 9 cents = theCents; 10 } 11 void currency::setValue(double theAmount) 12 { 13 if (theAmount < 0) 14 { 15 sign = minus; 16 theAmount = -theAmount; 17 } 18 else 19 { 20 sign = plus; 21 } 22 dollars = (unsigned long)theAmount; 23 cents = (unsigned int)((theAmount + 0.001 - dollars) * 100); 24 }
void currency::setValue(signType theSign, unsigned long theDollars, unsigned int theCents)
这个函数用于设置Currency对象的值,接受三个参数:theSign
(符号)、theDollars
(美元)和theCents
(美分)。
- 首先,它检查
theCents
的值是否大于99,如果是,则抛出一个字符串异常:"Cents should be < 100",表示美分应该小于100。 - 然后,它将传入的参数赋值给对象的对应数据成员:
theSign
赋值给sign
,theDollars
赋值给dollars
,theCents
赋值给cents
。
void currency::setValue(double theAmount)
这个函数也用于设置Currency对象的值,接受一个参数:theAmount
(金额)。
- 首先,它检查
theAmount
的值是否小于0。如果是,表示金额为负数,于是将sign
设置为minus
,并将theAmount
取绝对值(变为正数)。 - 如果
theAmount
大于等于0,表示金额为正数,于是将sign
设置为plus
。 - 然后,它将
theAmount
的整数部分转换为unsigned long
类型,并赋值给dollars
。 - 接着,它计算
theAmount
的小数部分,将其乘以100并转换为unsigned int
类型,赋值给cents
。
这两个函数分别通过不同的参数类型来设置Currency对象的值。第一个函数通过指定符号、美元和美分的具体值来设置对象,而第二个函数通过一个总金额来设置对象,根据金额的正负设置符号,并将金额拆分为美元和美分的部分。
这两个函数都用于设置Currency对象的值,并根据输入的不同情况进行相应的处理和验证。
下面是方法add的代码:
1 currency currency::add(const currency &x) const 2 { 3 long a1, a2, a3; 4 currency result; 5 a1 = dollars * 100 + cents; 6 if (sign == minus) 7 { 8 a1 = -a1; 9 } 10 a2 = x.dollars * 100 + x.cents; 11 if (x.sign == minus) 12 { 13 a2 = -a2; 14 } 15 a3 = a1 + a2; 16 if (a3 < 0) 17 { 18 result.sign = minus; 19 a3 = -a3; 20 } 21 else 22 { 23 result.sign = plus; 24 } 25 result.dollars = a3 / 100; 26 result.cents = a3 - result.dollars * 100; 27 return result; 28 }
这段代码实现了currency
类的成员函数add(const currency &x)
,用于将当前对象与另一个currency
对象相加并返回结果。
-
首先,定义了变量
a1
、a2
和a3
,用于存储计算过程中的中间结果,以及一个currency
类型的变量result
,用于存储最终的计算结果。 -
接下来,将当前对象的美元和美分的值合并为一个长整型数值
a1
。如果当前对象的符号是minus
(负号),则将a1
取相反数。 -
类似地,将另一个
currency
对象x
的美元和美分的值合并为一个长整型数值a2
。如果x
的符号是minus
(负号),则将a2
取相反数。 -
计算
a3
,即将a1
和a2
相加。 -
如果
a3
小于0,表示结果为负数,将result
的符号设置为minus
,并将a3
取相反数。 -
如果
a3
大于等于0,表示结果为正数,将result
的符号设置为plus
。 -
根据计算得到的
a3
,将其除以100得到结果的美元部分,并将结果赋值给result
的dollars
成员变量。 -
然后,通过
a3
减去result.dollars
乘以100得到结果的美分部分,并将结果赋值给result
的cents
成员变量。 -
最后,返回存储结果的
result
对象。
该函数的作用是将当前对象与另一个currency
对象相加,并将结果以新的currency
对象的形式返回。计算过程中,将金额转换为以美元和美分合并的长整型数值进行处理,以避免精度问题。根据计算结果的正负,设置新对象的符号,并将金额分解为美元和美分的部分。
下面是increment和output的代码
1 currency ¤cy::increment(const currency &x) 2 { 3 *this = add(x); 4 return *this; 5 } 6 void currency::output() const 7 { 8 if (sign == minus) 9 { 10 std::cout << "-"; 11 } 12 std::cout << "$" << dollars << "."; 13 if (cents < 10) 14 { 15 std::cout << "0"; 16 } 17 std::cout << cents; 18 }
currency ¤cy::increment(const currency &x)
这个函数用于将当前对象与另一个currency
对象相加,并将结果赋值给当前对象自身。它首先调用之前定义的add(const currency &x)
函数来执行加法操作,并通过赋值操作符将结果赋给*this
,即当前对象。然后,它返回当前对象的引用。
void currency::output() const
这个函数用于在标准输出上打印当前对象的值。它首先根据对象的符号(sign
)判断是否需要打印负号,并输出$
作为美元符号。然后,它打印对象的美元值(dollars
)。接着,它根据对象的美分值(cents
)的大小判断是否需要在输出中加上前导零。最后,它打印对象的美分值。
下面是一种新的描述class currency的方法 这里就不在赘述了 想了解的可以看书p18-p20
由于我c++学的不好,就先从理解重载开始吧。
当我们谈论重载(Overloading)时,指的是在同一个作用域内,根据不同的参数列表或参数类型,为同一个函数或操作符定义多个不同的版本。重载允许我们根据不同的情况使用相同的名称来执行不同的操作。
在C++中,函数重载和操作符重载都是允许的。
函数重载: 当你在同一个作用域内声明多个函数具有相同的名称,但是参数列表不同(参数个数、参数类型或参数顺序不同),这就是函数重载。编译器根据函数调用时传递的参数类型和数量来决定调用哪个函数。
1 void printNumber(int num); 2 void printNumber(double num); 3 void printNumber(std::string str);
上述代码中,printNumber
函数被重载了三次,分别接受int
、double
和std::string
类型的参数。根据传递的参数类型,编译器会自动选择相应的函数进行调用。
操作符重载: 操作符重载允许我们重新定义操作符在自定义类型上的行为。通过重载操作符,我们可以为自定义类型定义与内置类型类似的操作,使其具有更直观和一致的语法。
例如:
1 class Vector { 2 public: 3 int x, y; 4 5 Vector operator+(const Vector& other) const { 6 Vector result; 7 result.x = this->x + other.x; 8 result.y = this->y + other.y; 9 return result; 10 } 11 };
在上述代码中,我们重载了加法操作符+
,使得我们可以对Vector
对象进行加法操作,实现向量的相加。
对于currency类来说的话
-
算术运算符重载:
+
:currency operator+(const currency& other) const;
重载加法运算符,用于将两个currency
对象相加并返回结果。-
:currency operator-(const currency& other) const;
重载减法运算符,用于将一个currency
对象减去另一个currency
对象并返回结果。*
:currency operator*(double factor) const;
重载乘法运算符,用于将currency
对象乘以一个因子并返回结果。
-
比较运算符重载:
==
:bool operator==(const currency& other) const;
重载相等运算符,用于比较两个currency
对象是否相等。!=
:bool operator!=(const currency& other) const;
重载不等运算符,用于比较两个currency
对象是否不相等。<
:bool operator<(const currency& other) const;
重载小于运算符,用于比较一个currency
对象是否小于另一个currency
对象。>
:bool operator>(const currency& other) const;
重载大于运算符,用于比较一个currency
对象是否大于另一个currency
对象。
-
赋值运算符重载:
=
:currency& operator=(const currency& other);
重载赋值运算符,用于将一个currency
对象的值赋给另一个currency
对象。
代码就不多演示了
那么重载的意义在哪里呢
以下回答来自chatgpt:
友元和保护性类成员
- 友元函数的例子:
1 class currency { 2 // ... 3 friend void printCurrency(const currency& c); // 友元函数的声明 4 5 public: 6 // ... 7 }; 8 9 void printCurrency(const currency& c) { 10 // 友元函数可以访问currency类的私有和保护成员 11 std::cout << "Currency: " << c.sign << c.dollars << "." << c.cents << std::endl; 12 } 13 14 int main() { 15 currency c(plus, 10, 50); 16 printCurrency(c); // 调用友元函数,输出Currency对象的值 17 return 0; 18 }
在上述示例中,printCurrency
函数被声明为currency
类的友元函数。这意味着它可以访问currency
类的私有和保护成员。在main
函数中,我们创建了一个currency
对象c
,然后通过调用友元函数printCurrency(c)
来输出c
对象的值。
- 保护性类成员的例子:
1 class currency { 2 protected: 3 int getDollars() const { return dollars; } // 保护性类成员的声明 4 5 public: 6 // ... 7 }; 8 9 int main() { 10 currency c(plus, 10, 50); 11 int dollars = c.getDollars(); // 访问保护性类成员 12 // 其他操作... 13 return 0; 14 }
在上述示例中,
currency
类声明了一个保护性类成员函数getDollars()
,它返回私有成员变量dollars
的值。在main
函数中,我们创建了一个currency
对象c
,然后通过调用c.getDollars()
来访问保护性类成员函数并获取dollars
的值。这些示例说明了友元函数和保护性类成员的使用。友元函数可以访问类的私有和保护成员,而保护性类成员可以被派生类访问,但对于外部代码仍然是不可见的。这些特性提供了灵活性和封装性,使得类的设计更加可控和安全。
今天的复习就到这里吧 感觉还是和算法什么的不是满一样,后续会考虑更新我车队的规划路径的进度。
希望可以多坚持几天吧。