本帖最后由 45gfg9 于 2020-8-31 17:01 编辑
Prev: C++Ⅴ:类与对象
Next: C++Ⅶ:继承 & 多态
索引贴
答案帖
Prev: C++Ⅴ:类与对象
Next: C++Ⅶ:继承 & 多态
1 为类创建单独文件
1.1 创建新类
一般地,在单独的文件里定义你要用的新类是好习惯。这会让维护和阅读代码更简单。
要这样做的话,在CodeBlocks中这样做:
点击File->New->Class...
给你的新类起个名,取消选择"Has destructor"并选择"Header and implementation file shall be in same folder",然后点击"Create"按钮。

注意到有两个新文件已经添加到你的项目了:

Part题:
通常,哪两个文件定义一个类?
A. 头文件和主文件
B. 类文件和源文件
C. 头文件和源文件
D. 头文件和体文件
1.2 源文件 & 头文件(一)
头文件 (.h) 包含了函数声明(原型)和变量声明。
它现在包含我们新的MyClass类的模板,有一个默认构造函数。
MyClass.h
```cpp
#ifndef MYCLASS_H
#define MYCLASS_H
class MyClass
{
public:
MyClass();
protected:
private:
};
#endif // MYCLASS_H
```
类的实现和方法体放在源文件 (.cpp) 中。
目前它只包括一个空的构造函数。
MyClass.cpp
```cpp
#include "MyClass.h"
MyClass::MyClass()
{
//ctor
}
```
Part题:
头文件作用是?
A. 函数原型和变量声明
B. 只有构造函数
C. 函数源码
D. 什么都没有
1.3 作用域解析运算符
源文件 (.cpp) 中的双冒号叫做作用域解析运算符 ,在构造函数的定义中有用到:
```cpp
#include "MyClass.h"
MyClass::MyClass()
{
//ctor
}
```
作用域解析运算符用来定义一个特定的类的已经声明了的成员函数。记住,我们在头文件中定义构造函数原型。
Part题:
用选项填空,创建一个属于demo类的函数test。
```cpp
______ :: ______ ()
{
}
```
`test class make function demo`
1.4 源文件 & 头文件(二)
要在main中使用我们的类的话,我们需要包含头文件。
举例,如果在main中用我们新创建的MyClass:
```cpp
#include <iostream>
#include "MyClass.h"
using namespace std;
int main() {
MyClass obj;
}
```
Part题:
要使用类,main.cpp中要包含什么?
A. 类的源文件
B. 源文件和头文件都要
C. 类的头文件
2 析构函数
2.1 析构函数(一)
还记得构造函数么?它们是当对象被创建时调用的特殊成员函数。
析构函数 也是特殊函数。它们会在当对象被销毁或删除时调用。
Part题:
下面关于C++中构造函数和析构函数的表述中,哪条正确?
A. 构造函数在对象被创建时调用;析构函数当其被删除时调用
B. 构造函数在当对象被删除时调用
C. 析构函数在当对象被创建时调用
2.2 析构函数(二)
析构函数的名字也与类名一致,只是有一个波浪号 (~)前缀。析构函数不能返回值,也不能有参数。
```cpp
class MyClass {
public:
~MyClass() {
// some code
}
};
```
Part题:
什么是析构函数符号?
2.3 析构函数(三)
举个例子,我们来给我们的MyClass类声明一个析构函数,在MyClass.h头文件中:
```cpp
class MyClass
{
public:
MyClass();
~MyClass();
};
```
Part题:
用选项填空,给MyClass类声明一个析构函数。
```cpp
class MyClass {
public:
MyClass();
______;
};
```
`~MyClass() destructor() !MyClass() MyClass~~`
2.4 析构函数(四)
在头文件中声明析构函数之后,我们可以在源文件MyClass.cpp中写实现了:
```cpp
#include "MyClass.h"
#include <iostream>
using namespace std;
MyClass::MyClass()
{
cout<<"Constructor"<<endl;
}
MyClass::~MyClass()
{
cout<<"Destructor"<<endl;
}
```
注意到我们使用了<iostream>头文件,以来使用cout。
Part题:
用选项填空,给类MyClass声明一个析构函数,其向屏幕输出文字。
```cpp
MyClass:: ______ {
______ ______ "some text";
}
```
`>> cout ~MyClass() << destructor`
2.5 析构函数(五)
因为析构函数不能有参数,所以其不能被重载。
每个类只会有一个析构函数。
Part题:
下面的表述哪些正确?
A. 析构函数不能重载
B. 析构函数永远返回double
C. 析构函数不能有任何参数
D. 析构函数无名称
2.6 析构函数(六)
回到main吧。
```cpp
#include <iostream>
#include "MyClass.h"
using namespace std;
int main() {
MyClass obj;
return 0;
}
```
我们包含了类的头文件并创建了那个类型的对象。这会有如下输出:
```
Constructor
Destructor
```
程序运行时,它先创建一个对象并调用构造函数。程序执行完成后,对象被销毁,析构函数被调用。
Part题:
析构函数...
A. ...在对象被删除时调用
B. ...在构造函数没有被调用时调用
C. ...是成员变量
3 箭头成员运算符
3.1 #ifndef & #define
我们给我们的类创建了单独的头文件和源文件,头文件如下:
```cpp
#ifndef MYCLASS_H
#define MYCLASS_H
class MyClass
{
public:
MyClass();
protected:
private:
};
#endif // MYCLASS_H
```
ifndef意味如果没有被定义 。第一对语句告诉程序,如果还未定义MyClass头文件,就定义它。
endif结束这个条件。
Part题:
#ifndef和#define语句的意图是什么?
A. 让程序跑得更快
B. 防止同一头文件在一个文件内被包含多于一次
C. 是std命名空间的需要
3.2 成员函数
我们来给类创建一个示例函数myPrint()。
MyClass.h
```cpp
class MyClass
{
public:
MyClass();
void myPrint();
};
```
MyClass.cpp
```cpp
#include "MyClass.h"
#include <iostream>
using namespace std;
MyClass::MyClass() {
}
void MyClass::myPrint() {
cout <<"Hello"<<endl;
}
```
Part题:
用选项填空,在Sally类中创建myPrint函数原型。
```cpp
______ Sally {
______ :
Sally();
void ______ ();
};
```
`public myPrint function class prototype`
3.3 点运算符
接着来创建一个MyClass类型的对象,然后用点 (.) 运算符调用其myPrint()函数:
```cpp
#include "MyClass.h"
int main() {
MyClass obj;
obj.myPrint();
}
// 输出 "Hello"
```
Part题:
用选项填空,声明一个Sally类对象,并用点 (.) 运算符调用其myPrint成员函数。
```cpp
int main() {
______ obj;
______ . ______;
}
```
`class obj myPrint() dot Sally`
3.4 指针(四)
我们也可以用指针访问对象成员。
下面的指针指向obj对象。
```cpp
MyClass obj;
MyClass *ptr = &obj;
```
Part题:
填空声明一个指向obj的指针:
```cpp
Sally obj;
Sally _ sallyPtr = _ obj;
```
3.5 箭头成员运算符
箭头成员选择运算符 用于用指针访问对象成员。
```cpp
MyClass obj;
MyClass *ptr = &obj;
ptr->myPrint();
```
Part题:
填上缺失的箭头运算符来通过sallyPtr调用myPrint()函数。
```cpp
Sally obj;
Sally *sallyObj = &obj;
sallyPtr __ myPrint();
```
4 常量对象
4.1 常量
常量是有固定值的表达式。在程序运行时,它不能被改变。
用const关键字定义常量。
```cpp
const int x = 42;
```
Part题:
填空声明double类型的常量var。
```cpp
_____ double ___ = 3.4;
```
4.2 常量对象(一)
如内置类型一样,我们也可以用const关键字创建常量对象。
```cpp
const MyClass obj;
```
所有的const变量都必须在创建时初始化。对类而言,初始化由构造函数完成。如果函数不是用带参构造函数创建的,那么必须提供一个公有默认构造函数——如果没有提供公有默认构造函数,则会产生编译错误。
当const对象通过构造函数创建时,你不能修改对象的成员变量。这既包括对public成员变量的修改,也包括通过成员函数修改成员变量。
Part题:
用选项填空,声明一个Student类型的对象st,并调用其printAge()函数。
```cpp
______ st;
st. ______;
```
`~Student class st Student printAge()`
4.3 常量对象(二)
只有非const对象才能调用非const函数。
一个常量对象无法调用普通函数。因此,要使用常量对象,必须需要一个常量函数。
要声明一个函数为const成员,函数原型的右圆括号后,必须跟const关键字。对于那些在类定义外定义的const成员函数,const关键字必须在函数原型和定义中都要有。举例:
MyClass.h
```cpp
MyClass.h
class MyClass
{
public:
void myPrint() const;
};
```
MyClass.cpp
```cpp
#include "MyClass.h"
#include <iostream>
using namespace std;
void MyClass::myPrint() const {
cout <<"Hello"<<endl;
}
```
现在myPrint()函数是一个常成员函数了。既然如此,我们的常量对象可以调用它了:
```cpp
int main() {
const MyClass obj;
obj.myPrint();
}
// 输出 "Hello"
```
Part题:
填上缺失的关键字,为Student类声明一个常成员函数printAge()。
```cpp
_____ Student {
public:
void printAge() _____ ;
};
```
4.4 常量对象(三)
尝试在常量对象内调用普通函数会产生错误。
另外,当任何const成员函数尝试改变成员变量或调用非const成员函数时产生编译错误。
Part题:
常成员函数...
A. ...不能被调用
B. ...能修改非const数据成员
C. ...不能修改任何非const数据成员
5 成员初始化
5.1 成员初始化(一)
回忆一下,常量是值无法改变的变量,所有const变量都必须在创建时初始化。
C++提供了一种顺手的语法用来初始化类成员,叫做成员初始化列表 (也叫构造函数初始化器 )。
Part题:
常量...
A. ...必须不被初始化
B. ...不能被改变
C. ...能被修改
5.2 成员初始化(二)
考虑下面的类:
```cpp
class MyClass {
public:
MyClass(int a, int b) {
regVar = a;
constVar = b;
}
private:
int regVar;
const int constVar;
};
```
此类有两个成员变量,regVar和constVar。它也有一个构造函数接受两个参数,用于初始化成员变量。
运行此代码会产生错误,因为它其中的一个成员变量是常量,不能在声明后改变值。
在这种例子中,成员初始化列表能用来给成员变量赋值。
```cpp
class MyClass {
public:
MyClass(int a, int b)
: regVar(a), constVar(b)
{
}
private:
int regVar;
const int constVar;
};
```
注意到上面语法,初始化列表跟着构造函数形参。列表由冒号 (:) 开始,接着列出了要初始化的变量和要赋的值,以逗号分隔。
用语法变量(值)赋值。
Part题:
你有一个类Student,有两个成员age和num。填空用对应的值在构造函数初始化器中初始化成员。
```cpp
Student::Student(int a, int b)
_ age(a),
___ (b) {
}
```
5.3 成员初始化(三)
让我们用独立头文件和源文件重写前例。
MyClass.h
```cpp
class MyClass {
public:
MyClass(int a, int b);
private:
int regVar;
const int constVar;
};
```
MyClass.cpp
```cpp
MyClass::MyClass(int a, int b)
: regVar(a), constVar(b)
{
cout << regVar << endl;
cout << constVar << endl;
}
```
我们在构造函数中添加了cout语句来输出成员变量的值。
下一步就是在main创建一个我们类的对象,并用构造函数赋值。
```cpp
#include "MyClass.h"
int main() {
MyClass obj(42, 33);
}
/*输出
42
33
*/
```
Part题:
用选项填空,在构造函数初始化列表中初始化成员,然后在构造函数体内输出。
```cpp
Student:: ______ (int a, int b)
: regVar(a),
constVar(b)
{cout << ______ <<
constVar ______ endl;
}
```
`regVar :: << >> Student`
5.4 成员初始化(四)
成员初始化列表可以用在普通变量上,必须用在常量上。
Part题:
常成员变量...
A. ...必须在构造函数体内初始化
B. ...必须在构造函数初始化列表中初始化
C. ...怎么初始化随你
6 组合(第一部分)
6.1 组合(一)
在现实生活中,复杂对象基本是用更小更简单的对象建造的。举例,一辆车用金属框架、引擎、轮胎,和大量其他部件组装。这个过程叫组合 。
在C++中,对象组合是通过将类作为其他类的成员而完成的。
这个示例程序演示了组合的使用。它包含Person和Birthday类,每个Person都会有一个Birthday对象作为其成员。
Birthday:
```cpp
class Birthday {
public:
Birthday(int m, int d, int y)
: month(m), day(d), year(y)
{
}
private:
int month;
int day;
int year;
};
```
我们的Birthday类有三个成员变量。它也有通过成员初始化列表初始化成员的构造函数。
Part题:
填空在构造函数初始化列表中初始化Birthday类成员。
```cpp
Birthday::Birthday(int m, int d, int y)
_ month(m),
day(d),
year( _ )
{
}
```
6.2 组合(二)
让我们给Birthday类也加一个printDate()函数。
```cpp
class Birthday {
public:
Birthday(int m, int d, int y)
: month(m), day(d), year(y)
{
}
void printDate()
{
cout<<month<<"/"<<day
<<"/"<<year<<endl;
}
private:
int month;
int day;
int year;
};
```
Part题:
用选项填空,创建类People,有三个私有整型成员:birthMonth,birthDay,birthYear。
```cpp
class ______ {
public:
People();
______
int ______ ;
int birthDay;
______ birthYear;
};
```
`public People private: int birthMonth var`
6.3 组合(三)
接着,我们可以创建Person类了,其包含Birthday类。
```cpp
#include <string>
#include "Birthday.h"
class Person {
public:
Person(string n, Birthday b)
: name(n),
bd(b)
{
}
private:
string name;
Birthday bd;
};
```
Person类有name和Birthday成员,和一个初始化它们的构造函数。
确保包含了对应的头文件。
Part题:
用选项填空,声明People类,有两个私有成员:类型string的name和类型Birthday的dateOfBirth。别忘记包含string类型的头文件和Birthday.h。
```cpp
#include < ______ >
#include " ______ "
class ______ {
public:
People(string n, Birthday bo);
private:
string name;
______ dateOfBirth;
};
```
`Birthday.h Birthday People string`
7 组合(第二部分)
7.1 组合(四)
现在,我们的Person类有Birthday类的成员了:
```cpp
class Person {
public:
Person(string n, Birthday b)
: name(n),
bd(b)
{
}
private:
string name;
Birthday bd;
};
```
Part题:
填空声明People的构造函数,接受两个参数并初始化其私有成员:name和dateOfBirth。
```cpp
People::People(string x, Birthday bo)
_ name( _ ), dateOfBirth(bo)
{
}
```
7.2 组合(五)
让我们来给Person类添加一个printInfo()函数,输出对象的数据:
```cpp
class Person {
public:
Person(string n, Birthday b)
: name(n),
bd(b)
{
}
void printInfo()
{
cout << name << endl;
bd.printDate();
}
private:
string name;
Birthday bd;
};
```
Part题:
用选项填空,定义printInfo()函数,输出“人”的 姓名 和生日 ,用dateOfBirth的printDate()函数。
```cpp
void People:: ______ () {
cout << name << endl;
______ . ______ ();
}
```
`printDate const printInfo dateOfBirth class`
7.3 组合(六)
现在我们有了Birthday类和Person类,可以去main,创建一个Birthday对象,并传给Person对象了。
```cpp
int main() {
Birthday bd(2, 21, 1985);
Person p("David", bd);
p.printInfo();
}
/*输出
David
2/21/1985
*/
```
我们创建了Birthday对象,代表日期1985年2月21日。接着,我们创建了一个Person对象,并将Birthday对象传给其构造函数。最终,我们用Person对象的printInfo()函数输出其数据。
Part题:
用选项填空,声明一个People类型的对象,其接受一个字符串作为第一个参数,一个Birthday对象作为第二个参数。在传递给People构造函数前,声明一个Birthday对象birthObj。
```cpp
______ birthObj(10, 30, 1989);
People john("John", ______ );
```
`birthObj Birthday string class People`
8 friend关键字
8.1 友元函数(一)
通常,一个类的私有成员不能被其外界访问。
然而,声明一个非成员函数为类的友元 允许它访问这个类的私有成员。这是通过在类中包含一个这个外部函数的声明,并用friend关键字放在声明前来达成的。
下例中,someFunc(),不是类的成员函数,是MyClass的友元,可以访问其私有成员。
```cpp
class MyClass {
public:
MyClass() {
regVar = 0;
}
private:
int regVar;
friend void someFunc(MyClass &obj);
};
```
Part题:
什么是声明友元的关键字?
8.2 友元函数(二)
someFunc()函数在类外以正常函数的方式定义。它接受一个MyClass类型的对象作为其形参,并有权访问那个对象的私有数据成员。
```cpp
class MyClass {
public:
MyClass() {
regVar = 0;
}
private:
int regVar;
friend void someFunc(MyClass &obj);
};
void someFunc(MyClass &obj) {
obj.regVar = 42;
cout << obj.regVar;
}
```
someFunc()函数改变了对象的私有成员并输出其值。
Part题:
填上缺失的关键字,使函数foo成为MyClass类的友元。
```cpp
class MyClass {
public:
______ void foo();
};
```
8.3 友元函数(三)
现在我们可以在main中创建对象并调用someFunc()函数了。
```cpp
int main() {
MyClass obj;
someFunc(obj);
}
//输出 42
```
someFunc()有权修改对象的私有成员并输出其值。
友元函数的典型用例是可能涉及访问两个不同类的私有成员的函数。
Part题:
友元函数...
A. ...只能修改公有成员
B. ...不能修改类的私有成员
C. ...可以修改包括私有成员在内的类的所有成员
9 this关键字
9.1 this(一)
C++中每个对象都可访问自己的地址,通过一种重要的指针,叫做this指针。
在成员函数中,this可以用来指代调用对象。
我们来创建一个示例类。
```cpp
class MyClass {
public:
MyClass(int a) : var(a)
{ }
private:
int var;
};
```
Part题:
用选项填空,声明一个Hannah类,有一个一参构造函数,一个printCrap()函数,和一个私有整型变量。
```cpp
class Hannah {
_____ :
Hannah(int);
void printCrap();
private:
______ h;
};
```
`int private public friend class`
9.2 this(二)
printInfo()方法提供了三种输出类的成员变量的方式。
```cpp
class MyClass {
public:
MyClass(int a) : var(a)
{ }
void printInfo() {
cout << var<<endl;
cout << this->var<<endl;
cout << (*this).var<<endl;
}
private:
int var;
};
```
三种方式输出相同。
Part题:
对于一个名为mem的成员,选择两项可以正确输出它的方式。
A. cout << this->mem;
B. cout << this>>mem;
C. cout << mem;
D. cout ** mem;
9.3 this(三)
要看到结果,我们可以创建一个我们类的对象并调用成员函数。
```cpp
#include <iostream>
using namespace std;
class MyClass {
public:
MyClass(int a) : var(a)
{ }
void printInfo() {
cout << var <<endl;
cout << this->var <<endl;
cout << (*this).var <<endl;
}
private:
int var;
};
int main() {
MyClass obj(42);
obj.printInfo();
}
/* 输出
42
42
42
*/
```
所有三种访问成员变量的方式都有效。
Part题:
什么是存储当前对象地址的关键字?
9.4 this(四)
你也许在想可以直接指定变量时,为什么要用this关键字。
this关键字在运算符重载 中有举足轻重的地位,接着我们会讲到。
Part题:
对于类MyClass,有一个私有成员名为mem,填空在printValue()函数中通过this关键字输出其值。
```cpp
void MyClass::printValue() {
cout << ____ -> ___;
}
```
10 运算符重载
10.1 运算符重载(一)
C++中大多运算符都可以被重定义或重载。
因此,用户定义的类型也可以使用各种运算符(例如你可以将两个对象相加)。
下表列出可以重载的运算符。

Part题:
用选项填空,声明类Sally,公有范围内只有构造函数。
```cpp
______ Sally
{
______ :
______ ();
};
```
`~Sally private public Sally class`
10.2 运算符重载(二)
让我们声明一个示例类,用于演示运算符重载:
```cpp
class MyClass {
public:
int var;
MyClass() {}
MyClass(int a)
: var(a) { }
};
```
我们的类有两个构造函数和一个成员变量。
Part题:
填空声明类Sally,有两个构造函数,一个是默认构造函数(无参),另一个有一个整型参数。
```cpp
_____ Sally
{
______ :
Sally();
Sally( ___ );
};
```
10.3 运算符重载(三)
重载的运算符是函数,由关键字operator后跟要重载运算符的符号定义。
被重载的运算符与其他函数类似,有返回类型和形参列表。
在我们的例子中,我们会重载+运算符。它会返回一个我们类的对象并接受一个我们类的对象作为其形参。
```cpp
class MyClass {
public:
int var;
MyClass() {}
MyClass(int a)
: var(a) { }
MyClass operator+(MyClass &obj) {
}
};
```
Part题:
哪个选项是在C++中重载运算符的关键字?
A. operator
B. this
C. overload_it
D. friend
10.4 运算符重载(四)
我们需要我们的+运算符返回一个新的MyClass对象,其成员变量等于两个对象成员变量之和。
```cpp
class MyClass {
public:
int var;
MyClass() {}
MyClass(int a)
: var(a) { }
MyClass operator+(MyClass &obj) {
MyClass res;
res.var= this->var+obj.var;
return res;
}
};
```
在这,我们声明了一个新res对象。之后我们将当前对象(this)和形参对象(obj)的成员变量之和赋给res对象的成员变量var。res对象作为结果返回。
这给我们在main中创建对象并用被重载的+运算符相加对象的能力。
```cpp
int main() {
MyClass obj1(12), obj2(55);
MyClass res = obj1+obj2;
cout << res.var;
}
//输出 67
```
Part题:
用选项填空,定义Test类的被重载的+运算符。
```cpp
Test Test:: ______ (Test obj) {
______ newObj;
newObj.mem =
mem ______ obj.mem;
return newObj;
}
```
`Test class + operator+ const`
本章测试
1. 填空声明Student类的析构函数。
```cpp
class Student {
public:
Student();
_ Student();
};
```
2. 填空声明指向st的指针,st类型是Student,之后通过指针调用printAge()。
```cpp
Student st;
Student _ stPtr = _ st;
stPtr __ printAge();
```
3. 填上缺失的关键字,声明一个Person类型的常量对象:
```cpp
_____ Person obj;
```
4. 对于类P,其有一个double型常成员weight,填空在构造函数初始化列表中初始化weight。
```cpp
P::P(double w) _
______ (w)
{
}
```
5. 什么是允许你指定一个函数是类的友元函数的关键字?
A. this
B. friendly
C. friend
D. make_friend
6. 对于类Test,其有两个私有成员名为mem和mem2,填空在printValues()函数内使用this关键字输出其值。
```cpp
void Test::printValues() {
cout << ____ -> ___;
cout << this __ mem2;
}
```
7. C++中重载运算符的关键字是?
Prev: C++Ⅴ:类与对象
Next: C++Ⅶ:继承 & 多态
2021.12 数据,可能有更多内容
索引贴
答案帖
Prev: C++Ⅴ:类与对象
Next: C++Ⅶ:继承 & 多态
1 为类创建单独文件
1.1 创建新类
一般地,在单独的文件里定义你要用的新类是好习惯。这会让维护和阅读代码更简单。
要这样做的话,在CodeBlocks中这样做:
点击File->New->Class...
给你的新类起个名,取消选择"Has destructor"并选择"Header and implementation file shall be in same folder",然后点击"Create"按钮。
注意到有两个新文件已经添加到你的项目了:
新文件就是我们新类的模板。MyClass.h是头文件。MyClass.cpp是源文件。
Part题:
通常,哪两个文件定义一个类?
A. 头文件和主文件
B. 类文件和源文件
C. 头文件和源文件
D. 头文件和体文件
1.2 源文件 & 头文件(一)
头文件 (.h) 包含了函数声明(原型)和变量声明。
它现在包含我们新的MyClass类的模板,有一个默认构造函数。
MyClass.h
```cpp
#ifndef MYCLASS_H
#define MYCLASS_H
class MyClass
{
public:
MyClass();
protected:
private:
};
#endif // MYCLASS_H
```
类的实现和方法体放在源文件 (.cpp) 中。
目前它只包括一个空的构造函数。
MyClass.cpp
```cpp
#include "MyClass.h"
MyClass::MyClass()
{
//ctor
}
```
头文件中的#ifndef和#define语句接下来的课程讲。
Part题:
头文件作用是?
A. 函数原型和变量声明
B. 只有构造函数
C. 函数源码
D. 什么都没有
1.3 作用域解析运算符
源文件 (.cpp) 中的双冒号叫做
```cpp
#include "MyClass.h"
MyClass::MyClass()
{
//ctor
}
```
作用域解析运算符用来定义一个特定的类的已经声明了的成员函数。记住,我们在头文件中定义构造函数原型。
所以基本上讲,MyClass::MyClass()指的是MyClass()成员函数——或者在本例中,MyClass类的构造函数。
Part题:
用选项填空,创建一个属于demo类的函数test。
```cpp
______ :: ______ ()
{
}
```
`test class make function demo`
1.4 源文件 & 头文件(二)
要在main中使用我们的类的话,我们需要包含头文件。
举例,如果在main中用我们新创建的MyClass:
```cpp
#include <iostream>
#include "MyClass.h"
using namespace std;
int main() {
MyClass obj;
}
```
头文件声明了类(或者不管实现的是什么)会"做什么",而cpp源文件定义了它"如何做"。
Part题:
要使用类,main.cpp中要包含什么?
A. 类的源文件
B. 源文件和头文件都要
C. 类的头文件
2 析构函数
2.1 析构函数(一)
还记得构造函数么?它们是当对象被创建时调用的特殊成员函数。
对象在离开定义域时会被销毁,或者将delete表达式应用到指向这个类对象的指针时会被删除。
Part题:
下面关于C++中构造函数和析构函数的表述中,哪条正确?
A. 构造函数在对象被创建时调用;析构函数当其被删除时调用
B. 构造函数在当对象被删除时调用
C. 析构函数在当对象被创建时调用
2.2 析构函数(二)
析构函数的名字也与类名一致,只是有一个波浪号 (~)前缀。析构函数不能返回值,也不能有参数。
```cpp
class MyClass {
public:
~MyClass() {
// some code
}
};
```
析构函数在程序中释放资源时非常有用。这可以包括关闭文件、释放内存等等。
Part题:
什么是析构函数符号?
2.3 析构函数(三)
举个例子,我们来给我们的MyClass类声明一个析构函数,在MyClass.h头文件中:
```cpp
class MyClass
{
public:
MyClass();
~MyClass();
};
```
这个我们的MyClass类声明了一个析构函数。
Part题:
用选项填空,给MyClass类声明一个析构函数。
```cpp
class MyClass {
public:
MyClass();
______;
};
```
`~MyClass() destructor() !MyClass() MyClass~~`
2.4 析构函数(四)
在头文件中声明析构函数之后,我们可以在源文件MyClass.cpp中写实现了:
```cpp
#include "MyClass.h"
#include <iostream>
using namespace std;
MyClass::MyClass()
{
cout<<"Constructor"<<endl;
}
MyClass::~MyClass()
{
cout<<"Destructor"<<endl;
}
```
注意到我们使用了<iostream>头文件,以来使用cout。
Part题:
用选项填空,给类MyClass声明一个析构函数,其向屏幕输出文字。
```cpp
MyClass:: ______ {
______ ______ "some text";
}
```
`>> cout ~MyClass() << destructor`
2.5 析构函数(五)
因为析构函数不能有参数,所以其不能被重载。
每个类只会有一个析构函数。
析构函数不是必须的;如果不需要用就可以不用定义。
Part题:
下面的表述哪些正确?
A. 析构函数不能重载
B. 析构函数永远返回double
C. 析构函数不能有任何参数
D. 析构函数无名称
2.6 析构函数(六)
回到main吧。
```cpp
#include <iostream>
#include "MyClass.h"
using namespace std;
int main() {
MyClass obj;
return 0;
}
```
我们包含了类的头文件并创建了那个类型的对象。这会有如下输出:
```
Constructor
Destructor
```
程序运行时,它先创建一个对象并调用构造函数。程序执行完成后,对象被销毁,析构函数被调用。
我们在构造函数中输出了"Constructor",在析构函数中输出了"Destructor"。
Part题:
析构函数...
A. ...在对象被删除时调用
B. ...在构造函数没有被调用时调用
C. ...是成员变量
3 箭头成员运算符
3.1 #ifndef & #define
我们给我们的类创建了单独的头文件和源文件,头文件如下:
```cpp
#ifndef MYCLASS_H
#define MYCLASS_H
class MyClass
{
public:
MyClass();
protected:
private:
};
#endif // MYCLASS_H
```
ifndef意味
endif结束这个条件。
这会防止同一个头文件被包含超过一次。
Part题:
#ifndef和#define语句的意图是什么?
A. 让程序跑得更快
B. 防止同一头文件在一个文件内被包含多于一次
C. 是std命名空间的需要
3.2 成员函数
我们来给类创建一个示例函数myPrint()。
MyClass.h
```cpp
class MyClass
{
public:
MyClass();
void myPrint();
};
```
MyClass.cpp
```cpp
#include "MyClass.h"
#include <iostream>
using namespace std;
MyClass::MyClass() {
}
void MyClass::myPrint() {
cout <<"Hello"<<endl;
}
```
因为myPrint()是普通成员函数,所以在声明和定义中都必须标注其返回类型。
Part题:
用选项填空,在Sally类中创建myPrint函数原型。
```cpp
______ Sally {
______ :
Sally();
void ______ ();
};
```
`public myPrint function class prototype`
3.3 点运算符
接着来创建一个MyClass类型的对象,然后用点 (.) 运算符调用其myPrint()函数:
```cpp
#include "MyClass.h"
int main() {
MyClass obj;
obj.myPrint();
}
// 输出 "Hello"
```
Part题:
用选项填空,声明一个Sally类对象,并用点 (.) 运算符调用其myPrint成员函数。
```cpp
int main() {
______ obj;
______ . ______;
}
```
`class obj myPrint() dot Sally`
3.4 指针(四)
我们也可以用指针访问对象成员。
下面的指针指向obj对象。
```cpp
MyClass obj;
MyClass *ptr = &obj;
```
指针类型是MyClass,其指向那个类型的对象。
Part题:
填空声明一个指向obj的指针:
```cpp
Sally obj;
Sally _ sallyPtr = _ obj;
```
3.5 箭头成员运算符
```cpp
MyClass obj;
MyClass *ptr = &obj;
ptr->myPrint();
```
当使用对象时,用点成员选择运算符 (.)。
当使用指向对象的指针时,使用箭头成员选择运算符 (->)。
当使用指向对象的指针时,使用箭头成员选择运算符 (->)。
Part题:
填上缺失的箭头运算符来通过sallyPtr调用myPrint()函数。
```cpp
Sally obj;
Sally *sallyObj = &obj;
sallyPtr __ myPrint();
```
4 常量对象
4.1 常量
常量是有固定值的表达式。在程序运行时,它不能被改变。
用const关键字定义常量。
```cpp
const int x = 42;
```
任何常量都必须在创建时初始化。
Part题:
填空声明double类型的常量var。
```cpp
_____ double ___ = 3.4;
```
4.2 常量对象(一)
如内置类型一样,我们也可以用const关键字创建常量对象。
```cpp
const MyClass obj;
```
所有的const变量都必须在创建时初始化。对类而言,初始化由构造函数完成。如果函数不是用带参构造函数创建的,那么必须提供一个公有默认构造函数——如果没有提供公有默认构造函数,则会产生编译错误。
当const对象通过构造函数创建时,你不能修改对象的成员变量。这既包括对public成员变量的修改,也包括通过成员函数修改成员变量。
当你用const声明对象时,在对象生命期内无法改变其数据成员。
Part题:
用选项填空,声明一个Student类型的对象st,并调用其printAge()函数。
```cpp
______ st;
st. ______;
```
`~Student class st Student printAge()`
4.3 常量对象(二)
只有非const对象才能调用非const函数。
一个常量对象无法调用普通函数。因此,要使用常量对象,必须需要一个常量函数。
要声明一个函数为const成员,函数原型的右圆括号后,必须跟const关键字。对于那些在类定义外定义的const成员函数,const关键字必须在函数原型和定义中都要有。举例:
MyClass.h
```cpp
MyClass.h
class MyClass
{
public:
void myPrint() const;
};
```
MyClass.cpp
```cpp
#include "MyClass.h"
#include <iostream>
using namespace std;
void MyClass::myPrint() const {
cout <<"Hello"<<endl;
}
```
现在myPrint()函数是一个常成员函数了。既然如此,我们的常量对象可以调用它了:
```cpp
int main() {
const MyClass obj;
obj.myPrint();
}
// 输出 "Hello"
```
Part题:
填上缺失的关键字,为Student类声明一个常成员函数printAge()。
```cpp
_____ Student {
public:
void printAge() _____ ;
};
```
4.4 常量对象(三)
尝试在常量对象内调用普通函数会产生错误。
另外,当任何const成员函数尝试改变成员变量或调用非const成员函数时产生编译错误。
定义常量对象和常函数确保对应的数据成员不会被意外修改。
Part题:
常成员函数...
A. ...不能被调用
B. ...能修改非const数据成员
C. ...不能修改任何非const数据成员
5 成员初始化
5.1 成员初始化(一)
回忆一下,常量是值无法改变的变量,所有const变量都必须在创建时初始化。
C++提供了一种顺手的语法用来初始化类成员,叫做
Part题:
常量...
A. ...必须不被初始化
B. ...不能被改变
C. ...能被修改
5.2 成员初始化(二)
考虑下面的类:
```cpp
class MyClass {
public:
MyClass(int a, int b) {
regVar = a;
constVar = b;
}
private:
int regVar;
const int constVar;
};
```
此类有两个成员变量,regVar和constVar。它也有一个构造函数接受两个参数,用于初始化成员变量。
运行此代码会产生错误,因为它其中的一个成员变量是常量,不能在声明后改变值。
在这种例子中,成员初始化列表能用来给成员变量赋值。
```cpp
class MyClass {
public:
MyClass(int a, int b)
: regVar(a), constVar(b)
{
}
private:
int regVar;
const int constVar;
};
```
注意到上面语法,初始化列表跟着构造函数形参。列表由冒号 (:) 开始,接着列出了要初始化的变量和要赋的值,以逗号分隔。
用语法变量(值)赋值。
初始化列表消除了在构造函数体内显式赋值的必要。而且,初始化列表不需以分号结束。
Part题:
你有一个类Student,有两个成员age和num。填空用对应的值在构造函数初始化器中初始化成员。
```cpp
Student::Student(int a, int b)
_ age(a),
___ (b) {
}
```
5.3 成员初始化(三)
让我们用独立头文件和源文件重写前例。
MyClass.h
```cpp
class MyClass {
public:
MyClass(int a, int b);
private:
int regVar;
const int constVar;
};
```
MyClass.cpp
```cpp
MyClass::MyClass(int a, int b)
: regVar(a), constVar(b)
{
cout << regVar << endl;
cout << constVar << endl;
}
```
我们在构造函数中添加了cout语句来输出成员变量的值。
下一步就是在main创建一个我们类的对象,并用构造函数赋值。
```cpp
#include "MyClass.h"
int main() {
MyClass obj(42, 33);
}
/*输出
42
33
*/
```
构造函数用于创建对象,通过成员初始化列表用形参给成员变量赋值。
Part题:
用选项填空,在构造函数初始化列表中初始化成员,然后在构造函数体内输出。
```cpp
Student:: ______ (int a, int b)
: regVar(a),
constVar(b)
{cout << ______ <<
constVar ______ endl;
}
```
`regVar :: << >> Student`
5.4 成员初始化(四)
成员初始化列表可以用在普通变量上,必须用在常量上。
即使成员变量不是常量,用成员初始化语法也很有用。
Part题:
常成员变量...
A. ...必须在构造函数体内初始化
B. ...必须在构造函数初始化列表中初始化
C. ...怎么初始化随你
6 组合(第一部分)
6.1 组合(一)
在现实生活中,复杂对象基本是用更小更简单的对象建造的。举例,一辆车用金属框架、引擎、轮胎,和大量其他部件组装。这个过程叫
在C++中,对象组合是通过将类作为其他类的成员而完成的。
这个示例程序演示了组合的使用。它包含Person和Birthday类,每个Person都会有一个Birthday对象作为其成员。
Birthday:
```cpp
class Birthday {
public:
Birthday(int m, int d, int y)
: month(m), day(d), year(y)
{
}
private:
int month;
int day;
int year;
};
```
我们的Birthday类有三个成员变量。它也有通过成员初始化列表初始化成员的构造函数。
为了简洁,这个类在单文件中声明。你也可以分开源文件和头文件。
Part题:
填空在构造函数初始化列表中初始化Birthday类成员。
```cpp
Birthday::Birthday(int m, int d, int y)
_ month(m),
day(d),
year( _ )
{
}
```
6.2 组合(二)
让我们给Birthday类也加一个printDate()函数。
```cpp
class Birthday {
public:
Birthday(int m, int d, int y)
: month(m), day(d), year(y)
{
}
void printDate()
{
cout<<month<<"/"<<day
<<"/"<<year<<endl;
}
private:
int month;
int day;
int year;
};
```
这给我们的Birthday类添加了printDate()函数。
Part题:
用选项填空,创建类People,有三个私有整型成员:birthMonth,birthDay,birthYear。
```cpp
class ______ {
public:
People();
______
int ______ ;
int birthDay;
______ birthYear;
};
```
`public People private: int birthMonth var`
6.3 组合(三)
接着,我们可以创建Person类了,其包含Birthday类。
```cpp
#include <string>
#include "Birthday.h"
class Person {
public:
Person(string n, Birthday b)
: name(n),
bd(b)
{
}
private:
string name;
Birthday bd;
};
```
Person类有name和Birthday成员,和一个初始化它们的构造函数。
确保包含了对应的头文件。
下一节继续讲组合!
Part题:
用选项填空,声明People类,有两个私有成员:类型string的name和类型Birthday的dateOfBirth。别忘记包含string类型的头文件和Birthday.h。
```cpp
#include < ______ >
#include " ______ "
class ______ {
public:
People(string n, Birthday bo);
private:
string name;
______ dateOfBirth;
};
```
`Birthday.h Birthday People string`
7 组合(第二部分)
7.1 组合(四)
现在,我们的Person类有Birthday类的成员了:
```cpp
class Person {
public:
Person(string n, Birthday b)
: name(n),
bd(b)
{
}
private:
string name;
Birthday bd;
};
```
组合常被用在有包含关系的对象上,如“人 有生日 ”。
Part题:
填空声明People的构造函数,接受两个参数并初始化其私有成员:name和dateOfBirth。
```cpp
People::People(string x, Birthday bo)
_ name( _ ), dateOfBirth(bo)
{
}
```
7.2 组合(五)
让我们来给Person类添加一个printInfo()函数,输出对象的数据:
```cpp
class Person {
public:
Person(string n, Birthday b)
: name(n),
bd(b)
{
}
void printInfo()
{
cout << name << endl;
bd.printDate();
}
private:
string name;
Birthday bd;
};
```
注意到我们可以调用成员bd的printDate()函数,因为它是Birthday类型,且定义了那个函数。
Part题:
用选项填空,定义printInfo()函数,输出
```cpp
void People:: ______ () {
cout << name << endl;
______ . ______ ();
}
```
`printDate const printInfo dateOfBirth class`
7.3 组合(六)
现在我们有了Birthday类和Person类,可以去main,创建一个Birthday对象,并传给Person对象了。
```cpp
int main() {
Birthday bd(2, 21, 1985);
Person p("David", bd);
p.printInfo();
}
/*输出
David
2/21/1985
*/
```
我们创建了Birthday对象,代表日期1985年2月21日。接着,我们创建了一个Person对象,并将Birthday对象传给其构造函数。最终,我们用Person对象的printInfo()函数输出其数据。
一般来说,组合旨在将每个类保持得相对简单明了,只执行一项任务。这也允许每个子对象变得自包含,确保了重用性(我们可在其他类中使用Birthday类)。
Part题:
用选项填空,声明一个People类型的对象,其接受一个字符串作为第一个参数,一个Birthday对象作为第二个参数。在传递给People构造函数前,声明一个Birthday对象birthObj。
```cpp
______ birthObj(10, 30, 1989);
People john("John", ______ );
```
`birthObj Birthday string class People`
8 friend关键字
8.1 友元函数(一)
通常,一个类的私有成员不能被其外界访问。
然而,声明一个非成员函数为类的
下例中,someFunc(),不是类的成员函数,是MyClass的友元,可以访问其私有成员。
```cpp
class MyClass {
public:
MyClass() {
regVar = 0;
}
private:
int regVar;
friend void someFunc(MyClass &obj);
};
```
注意到我们给函数传递对象时,我们需要用&运算符通过引用传递。
Part题:
什么是声明友元的关键字?
8.2 友元函数(二)
someFunc()函数在类外以正常函数的方式定义。它接受一个MyClass类型的对象作为其形参,并有权访问那个对象的私有数据成员。
```cpp
class MyClass {
public:
MyClass() {
regVar = 0;
}
private:
int regVar;
friend void someFunc(MyClass &obj);
};
void someFunc(MyClass &obj) {
obj.regVar = 42;
cout << obj.regVar;
}
```
someFunc()函数改变了对象的私有成员并输出其值。
要使其对象可访问,类需要在其定义内将函数声明为友元。如果类没有“将访问权限给出”,你就不能“使”这个函数成为类的友元。
Part题:
填上缺失的关键字,使函数foo成为MyClass类的友元。
```cpp
class MyClass {
public:
______ void foo();
};
```
8.3 友元函数(三)
现在我们可以在main中创建对象并调用someFunc()函数了。
```cpp
int main() {
MyClass obj;
someFunc(obj);
}
//输出 42
```
someFunc()有权修改对象的私有成员并输出其值。
友元函数的典型用例是可能涉及访问两个不同类的私有成员的函数。
你可以在任意数量的类中声明一个函数是其友元。
与友元函数类似,你可以定义友元类,其有权限访问另一个类的私有成员。
与友元函数类似,你可以定义友元类,其有权限访问另一个类的私有成员。
Part题:
友元函数...
A. ...只能修改公有成员
B. ...不能修改类的私有成员
C. ...可以修改包括私有成员在内的类的所有成员
9 this关键字
9.1 this(一)
C++中每个对象都可访问自己的地址,通过一种重要的指针,叫做this指针。
在成员函数中,this可以用来指代调用对象。
我们来创建一个示例类。
```cpp
class MyClass {
public:
MyClass(int a) : var(a)
{ }
private:
int var;
};
```
友元函数没有this指针,因为友元不是类的成员。
Part题:
用选项填空,声明一个Hannah类,有一个一参构造函数,一个printCrap()函数,和一个私有整型变量。
```cpp
class Hannah {
_____ :
Hannah(int);
void printCrap();
private:
______ h;
};
```
`int private public friend class`
9.2 this(二)
printInfo()方法提供了三种输出类的成员变量的方式。
```cpp
class MyClass {
public:
MyClass(int a) : var(a)
{ }
void printInfo() {
cout << var<<endl;
cout << this->var<<endl;
cout << (*this).var<<endl;
}
private:
int var;
};
```
三种方式输出相同。
this是一个指向对象的指针,所以箭头选择符用于选择成员变量。
Part题:
对于一个名为mem的成员,选择两项可以正确输出它的方式。
A. cout << this->mem;
B. cout << this>>mem;
C. cout << mem;
D. cout ** mem;
9.3 this(三)
要看到结果,我们可以创建一个我们类的对象并调用成员函数。
```cpp
#include <iostream>
using namespace std;
class MyClass {
public:
MyClass(int a) : var(a)
{ }
void printInfo() {
cout << var <<endl;
cout << this->var <<endl;
cout << (*this).var <<endl;
}
private:
int var;
};
int main() {
MyClass obj(42);
obj.printInfo();
}
/* 输出
42
42
42
*/
```
所有三种访问成员变量的方式都有效。
注意到只有成员函数有this指针。
Part题:
什么是存储当前对象地址的关键字?
9.4 this(四)
你也许在想可以直接指定变量时,为什么要用this关键字。
this关键字在
Part题:
对于类MyClass,有一个私有成员名为mem,填空在printValue()函数中通过this关键字输出其值。
```cpp
void MyClass::printValue() {
cout << ____ -> ___;
}
```
10 运算符重载
10.1 运算符重载(一)
C++中大多运算符都可以被重定义或重载。
因此,用户定义的类型也可以使用各种运算符(例如你可以将两个对象相加)。
下表列出可以重载的运算符。
不能重载的运算符包括 :: | .* | . | ?:
Part题:
用选项填空,声明类Sally,公有范围内只有构造函数。
```cpp
______ Sally
{
______ :
______ ();
};
```
`~Sally private public Sally class`
10.2 运算符重载(二)
让我们声明一个示例类,用于演示运算符重载:
```cpp
class MyClass {
public:
int var;
MyClass() {}
MyClass(int a)
: var(a) { }
};
```
我们的类有两个构造函数和一个成员变量。
我们会重载+运算符,允许我们相加两个我们的类对象。
Part题:
填空声明类Sally,有两个构造函数,一个是默认构造函数(无参),另一个有一个整型参数。
```cpp
_____ Sally
{
______ :
Sally();
Sally( ___ );
};
```
10.3 运算符重载(三)
重载的运算符是函数,由关键字operator后跟要重载运算符的符号定义。
被重载的运算符与其他函数类似,有返回类型和形参列表。
在我们的例子中,我们会重载+运算符。它会返回一个我们类的对象并接受一个我们类的对象作为其形参。
```cpp
class MyClass {
public:
int var;
MyClass() {}
MyClass(int a)
: var(a) { }
MyClass operator+(MyClass &obj) {
}
};
```
现在,我们要定义函数的行为。
Part题:
哪个选项是在C++中重载运算符的关键字?
A. operator
B. this
C. overload_it
D. friend
10.4 运算符重载(四)
我们需要我们的+运算符返回一个新的MyClass对象,其成员变量等于两个对象成员变量之和。
```cpp
class MyClass {
public:
int var;
MyClass() {}
MyClass(int a)
: var(a) { }
MyClass operator+(MyClass &obj) {
MyClass res;
res.var= this->var+obj.var;
return res;
}
};
```
在这,我们声明了一个新res对象。之后我们将当前对象(this)和形参对象(obj)的成员变量之和赋给res对象的成员变量var。res对象作为结果返回。
这给我们在main中创建对象并用被重载的+运算符相加对象的能力。
```cpp
int main() {
MyClass obj1(12), obj2(55);
MyClass res = obj1+obj2;
cout << res.var;
}
//输出 67
```
有了重载运算符,你可以使用任何需要的自定义逻辑了。然而,无法改变运算符优先级、组合或操作数数量。
Part题:
用选项填空,定义Test类的被重载的+运算符。
```cpp
Test Test:: ______ (Test obj) {
______ newObj;
newObj.mem =
mem ______ obj.mem;
return newObj;
}
```
`Test class + operator+ const`
本章测试
1. 填空声明Student类的析构函数。
```cpp
class Student {
public:
Student();
_ Student();
};
```
2. 填空声明指向st的指针,st类型是Student,之后通过指针调用printAge()。
```cpp
Student st;
Student _ stPtr = _ st;
stPtr __ printAge();
```
3. 填上缺失的关键字,声明一个Person类型的常量对象:
```cpp
_____ Person obj;
```
4. 对于类P,其有一个double型常成员weight,填空在构造函数初始化列表中初始化weight。
```cpp
P::P(double w) _
______ (w)
{
}
```
5. 什么是允许你指定一个函数是类的友元函数的关键字?
A. this
B. friendly
C. friend
D. make_friend
6. 对于类Test,其有两个私有成员名为mem和mem2,填空在printValues()函数内使用this关键字输出其值。
```cpp
void Test::printValues() {
cout << ____ -> ___;
cout << this __ mem2;
}
```
7. C++中重载运算符的关键字是?
草
还以为你这个是索引
没想到是零散的东西
以及你可以细致讲一讲五三法则
还以为你这个是索引
没想到是零散的东西
以及你可以细致讲一讲五三法则
本帖最后由 langyo 于 2020-3-28 16:52 编辑
仔细看了下,你还缺了一堆东西
- =default 和 =delete 去哪里了?
- 拷贝构造函数需要更细致的讲解,配合五三讲比较好……
- 模板类有考虑讲不?
- 友元 operator 何时入手?
仔细看了下,你还缺了一堆东西
- =default 和 =delete 去哪里了?
- 拷贝构造函数需要更细致的讲解,配合五三讲比较好……
- 模板类有考虑讲不?
- 友元 operator 何时入手?
langyo 发表于 2020-3-28 16:51
仔细看了下,你还缺了一堆东西
- =default 和 =delete 去哪里了?
1. 2.
我觉得=default和=delete是进阶特性了awa
而且是C++11加的 而这篇教程大概还在用C++98(那咋办嘛)
3. 有(在C++Ⅷ了,不知道什么时候更新233)
4. 友元operator没得 以读者的水平应该可以自己写(逃)
百万贴大佬
这个帖子值得纪念,是一个有意义的教程帖子。
本帖最后由 Hueihuea 于 2020-3-28 22:53 编辑
咦 TID100W拍照留念
咦 挨卡了XD
咦 TID100W拍照留念
咦 挨卡了XD
大草
(本来以为至少是在茶馆出现的 教学贴爆冷门)
(反手给我打了一个大嘴巴子)
(本来以为至少是在茶馆出现的 教学贴爆冷门)
(反手给我打了一个大嘴巴子)
本帖最后由 1784234383 于 2020-3-30 16:19 编辑
还带章末测试 lz有心了
还带章末测试 lz有心了
虽然没看懂,但是C++在java版我的世界中有什么用吗(基岩版倒是C++开发的)
FireworkDLC 发表于 2020-3-29 07:40
虽然没看懂,但是C++在java版我的世界中有什么用吗(基岩版倒是C++开发的)
一般来说没有(捂脸)
这个系列与Minecraft无关,就是纯粹的编程入门
话说C++会不会有一天可以用于Java版的插件开发?(好像不太可能,跨平台就要重新编译了吧)
FireworkDLC 发表于 2020-3-29 07:40
虽然没看懂,但是C++在java版我的世界中有什么用吗(基岩版倒是C++开发的)
有用,写启动器
这个
只学到函数的我瑟瑟发抖
感觉我那本书太详细了堪比手册
这个知识密度太高了,改天当手册用吧
支持lz(还是百万帖子恭喜了
只学到函数的我瑟瑟发抖
感觉我那本书太详细了堪比手册
这个知识密度太高了,改天当手册用吧
支持lz(还是百万帖子恭喜了
恭喜成为100w贴,雾,
好评
我就是来水个帖子的
百万贴,恭喜恭喜
纯干货嗷,先收藏为敬了
纯干货嗷,先收藏为敬了
哇。没抢到锭,真难受
C艹乃秃头源泉
百万帖恭喜!
100w贴!!!
就是你抢了TID1000000? 原来是存草稿了,我们还以为被删了
本帖最后由 hjxhjx2 于 2020-4-4 14:55 编辑
谔谔C++OIer表示我不管这块
我只会算法QAQ(红黑树什么的)
只有2,4,5,8,9,10部分是我熟悉的。。。
(关于class。。。struct不香吗。。。class在OI中很废的...)
话说friend是不是可以用const代替来着啊。。。。
lz不打算另外出个帖子讲一下吗
谔谔C++OIer表示我不管这块
我只会算法QAQ(红黑树什么的)
只有2,4,5,8,9,10部分是我熟悉的。。。
(关于class。。。struct不香吗。。。class在OI中很废的...)
话说friend是不是可以用const代替来着啊。。。。
lz不打算另外出个帖子讲一下吗
dalao,百万,考古
[编辑于2080-1-1:]一亿前来考古
[编辑于2080-1-1:]一亿前来考古
hjxhjx2 发表于 2020-4-4 14:54
谔谔C++OIer表示我不管这块
我只会算法QAQ(红黑树什么的)
只有2,4,5,8,9,10部分是我熟悉的。。。
算法我完全痴呆(
friend和const是完全不同的两个东西啊
以及如果有需要的话 可能未来(大概)会开一个答疑帖吧
45gfg9 发表于 2020-4-5 23:01
算法我完全痴呆(
friend和const是完全不同的两个东西啊
但是我用STL重载运算符时似乎必须要加个friend或者const(??)
(好吧我对于一些语法并不了解。。。)
看完这个后 我竟不知道是先学java还是c++了
1000000 留念
百万考古留恋
自编2048
#include <bits/stdc++.h>
#include <windows.h>
#include <conio.h>
using namespace std;
long long a[1001][1001],s,h,n,pl;
int ct(int i,int j)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),8);
cout<<"║ ";
if(a[i][j]==0) cout<<" ";
if(a[i][j]==1)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED |FOREGROUND_GREEN | FOREGROUND_BLUE);
cout<<"□";
}
if(a[i][j]==2)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),3);
cout<<"□";
}
if(a[i][j]==4)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);
cout<<"□";
}
if(a[i][j]==8)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN |FOREGROUND_BLUE);
cout<<"□";
}
if(a[i][j]==16)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED |FOREGROUND_GREEN);
cout<<"□";
}
if(a[i][j]==32)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);
cout<<"■";
}
if(a[i][j]==64)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),3);
cout<<"■";
}
if(a[i][j]==128)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);
cout<<"■";
}
if(a[i][j]==256)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN |FOREGROUND_BLUE);
cout<<"■";
}
if(a[i][j]==512)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED |FOREGROUND_GREEN);
cout<<"■";
}
if(a[i][j]==1024)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);
cout<<"○";
}
if(a[i][j]==2048)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);
cout<<"◎";
}
}
int f(int x)
{
if(x==119)//up
{
for(int j=1;j<=n;j++)
for(int i=1;i<=n;i++)
{
int xl=i;
while(a[xl-1][j]==0&&xl>1)
{
a[xl-1][j]=a[xl][j];
a[xl][j]=0;
xl--;
}
if(a[xl-1][j]==a[xl][j])
{
a[xl][j]=0;
a[xl-1][j]*=2;
}
}
}
if(x==115)//down
{
for(int j=1;j<=n;j++)
for(int i=n;i>=1;i--)
{
int xl=i;
while(a[xl+1][j]==0&&xl<n)
{
a[xl+1][j]=a[xl][j];
a[xl][j]=0;
xl++;
}
if(a[xl+1][j]==a[xl][j])
{
a[xl][j]=0;
a[xl+1][j]*=2;
}
}
}
if(x==97)//left zuo
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
int yl=j;
while(a[i][yl-1]==0&&yl>1)
{
a[i][yl-1]=a[i][yl];
a[i][yl]=0;
yl--;
}
if(a[i][yl-1]==a[i][yl])
{
a[i][yl]=0;
a[i][yl-1]*=2;
}
}
}
if(x==100)//right you
{
for(int i=1;i<=n;i++)
for(int j=n;j>=1;j--)
{
int yl=j;
while(a[i][yl+1]==0&&yl<n)
{
a[i][yl+1]=a[i][yl];
a[i][yl]=0;
yl++;
}
if(a[i][yl+1]==a[i][yl])
{
a[i][yl]=0;
a[i][yl+1]*=2;
}
}
}
}
int main()
{
n=4;
while(s<n*n)
{
s=0; h=0;
int xl,s1=0,zl=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(!a[i][j]) s1++;
else h+=a[i][j];
if(s1==0)
{
cout<<"GAME OVER --------"<<h;
return 0;
}
srand(time(0));
xl=rand();
zl=rand();
zl%=2; xl%=s1;
zl++; xl++;
for(int i=1;i<=n;i++)
if(xl)
{
for(int j=1;j<=n;j++)
if(!a[i][j])
{
if(xl==1)
{
a[i][j]=zl;
}
xl--;
}
}
else break;
system("cls");
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),8);
cout<<"╔";
for(int i=1;i<=n-1;i++) cout<<" ═ ╦";
cout<<" ═ ╗"<<endl;
for(int i=1;i<=n-1;i++)
{
for(int j=1;j<=n;j++)
{
ct(i,j);
}
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),8);
cout<<"║"<<endl<<"╠";
for(int i=1;i<=n-1;i++) cout<<" ═ ╬";
cout<<" ═ ╣"<<endl;
}
for(int j=1;j<=n;j++)
{
ct(n,j);
}
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),8);
cout<<"║";
cout<<endl<<"╚";
for(int i=1;i<=n-1;i++) cout<<" ═ ╩";
cout<<" ═ ╝";
int z=getch();
f(z);
}
cout<<"GAME OVER --------"<<h;
return 0;
}
#include <bits/stdc++.h>
#include <windows.h>
#include <conio.h>
using namespace std;
long long a[1001][1001],s,h,n,pl;
int ct(int i,int j)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),8);
cout<<"║ ";
if(a[i][j]==0) cout<<" ";
if(a[i][j]==1)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED |FOREGROUND_GREEN | FOREGROUND_BLUE);
cout<<"□";
}
if(a[i][j]==2)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),3);
cout<<"□";
}
if(a[i][j]==4)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);
cout<<"□";
}
if(a[i][j]==8)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN |FOREGROUND_BLUE);
cout<<"□";
}
if(a[i][j]==16)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED |FOREGROUND_GREEN);
cout<<"□";
}
if(a[i][j]==32)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);
cout<<"■";
}
if(a[i][j]==64)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),3);
cout<<"■";
}
if(a[i][j]==128)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);
cout<<"■";
}
if(a[i][j]==256)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN |FOREGROUND_BLUE);
cout<<"■";
}
if(a[i][j]==512)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED |FOREGROUND_GREEN);
cout<<"■";
}
if(a[i][j]==1024)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);
cout<<"○";
}
if(a[i][j]==2048)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);
cout<<"◎";
}
}
int f(int x)
{
if(x==119)//up
{
for(int j=1;j<=n;j++)
for(int i=1;i<=n;i++)
{
int xl=i;
while(a[xl-1][j]==0&&xl>1)
{
a[xl-1][j]=a[xl][j];
a[xl][j]=0;
xl--;
}
if(a[xl-1][j]==a[xl][j])
{
a[xl][j]=0;
a[xl-1][j]*=2;
}
}
}
if(x==115)//down
{
for(int j=1;j<=n;j++)
for(int i=n;i>=1;i--)
{
int xl=i;
while(a[xl+1][j]==0&&xl<n)
{
a[xl+1][j]=a[xl][j];
a[xl][j]=0;
xl++;
}
if(a[xl+1][j]==a[xl][j])
{
a[xl][j]=0;
a[xl+1][j]*=2;
}
}
}
if(x==97)//left zuo
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
int yl=j;
while(a[i][yl-1]==0&&yl>1)
{
a[i][yl-1]=a[i][yl];
a[i][yl]=0;
yl--;
}
if(a[i][yl-1]==a[i][yl])
{
a[i][yl]=0;
a[i][yl-1]*=2;
}
}
}
if(x==100)//right you
{
for(int i=1;i<=n;i++)
for(int j=n;j>=1;j--)
{
int yl=j;
while(a[i][yl+1]==0&&yl<n)
{
a[i][yl+1]=a[i][yl];
a[i][yl]=0;
yl++;
}
if(a[i][yl+1]==a[i][yl])
{
a[i][yl]=0;
a[i][yl+1]*=2;
}
}
}
}
int main()
{
n=4;
while(s<n*n)
{
s=0; h=0;
int xl,s1=0,zl=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(!a[i][j]) s1++;
else h+=a[i][j];
if(s1==0)
{
cout<<"GAME OVER --------"<<h;
return 0;
}
srand(time(0));
xl=rand();
zl=rand();
zl%=2; xl%=s1;
zl++; xl++;
for(int i=1;i<=n;i++)
if(xl)
{
for(int j=1;j<=n;j++)
if(!a[i][j])
{
if(xl==1)
{
a[i][j]=zl;
}
xl--;
}
}
else break;
system("cls");
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),8);
cout<<"╔";
for(int i=1;i<=n-1;i++) cout<<" ═ ╦";
cout<<" ═ ╗"<<endl;
for(int i=1;i<=n-1;i++)
{
for(int j=1;j<=n;j++)
{
ct(i,j);
}
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),8);
cout<<"║"<<endl<<"╠";
for(int i=1;i<=n-1;i++) cout<<" ═ ╬";
cout<<" ═ ╣"<<endl;
}
for(int j=1;j<=n;j++)
{
ct(n,j);
}
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),8);
cout<<"║";
cout<<endl<<"╚";
for(int i=1;i<=n-1;i++) cout<<" ═ ╩";
cout<<" ═ ╝";
int z=getch();
f(z);
}
cout<<"GAME OVER --------"<<h;
return 0;
}
#在这里快速回复# 百万留名 你火了
2020年8月8日前来考古

感谢大佬翻译和转载