如何在C++中编写一个简单的类?

44

我已经阅读了很多有关C++类的教程,但它们漏掉了其他教程包括的一些内容。

请问有人可以展示一下如何编写和使用一个非常简单的C++类,其中包含可见性、方法以及一个简单的构造函数和析构函数吗?


31
一定是作业吧! - xian
7
我很难相信你在谷歌上无法找到有关那个主题的任何例子。下面的大多数例子只是从其他网站的教程中复制粘贴而来。 - Artem Barger
7
你肯定没有认真找过。 - DJ.
30
为什么不在这里回答呢?该网站在谷歌上的曝光率非常高,这是为很多人回答这个问题的机会。如果你觉得这个问题不值得回答,就忽略它并继续往下看。 - James McMahon
14
作为一个来自其他语言的开发者,我费尽心思地寻找可理解的答案来解决同样的问题。我发现了许多部分示例以及许多假设我不具备的秘密知识的示例。对于我的目的来说,这是一个合法且有帮助的问题。 - Craig.Feied
显示剩余3条评论
4个回答

33

以下示例来自于C++中的构造函数和析构函数,并对其进行了更好的解释:

#include <iostream>            // for cout and cin

class Cat                      // begin declaration of the class
{
  public:                      // begin public section
    Cat(int initialAge);       // constructor
    Cat(const Cat& copy_from); //copy constructor
    Cat& operator=(const Cat& copy_from); //copy assignment
    ~Cat();                    // destructor

    int GetAge() const;        // accessor function
    void SetAge(int age);      // accessor function
    void Meow();
 private:                      // begin private section
    int itsAge;                // member variable
    char * string;
};

// constructor of Cat,
Cat::Cat(int initialAge)
{
  itsAge = initialAge;
  string = new char[10]();
}

//copy constructor for making a new copy of a Cat
Cat::Cat(const Cat& copy_from) {
   itsAge = copy_from.itsAge;
   string = new char[10]();
   std::copy(copy_from.string+0, copy_from.string+10, string);
}

//copy assignment for assigning a value from one Cat to another
Cat& Cat::operator=(const Cat& copy_from) {
   itsAge = copy_from.itsAge;
   std::copy(copy_from.string+0, copy_from.string+10, string);
}

// destructor, just an example
Cat::~Cat()
{
    delete[] string;
}

// GetAge, Public accessor function
// returns value of itsAge member
int Cat::GetAge() const
{
   return itsAge;
}

// Definition of SetAge, public
// accessor function
 void Cat::SetAge(int age)
{
   // set member variable its age to
   // value passed in by parameter age
   itsAge = age;
}

// definition of Meow method
// returns: void
// parameters: None
// action: Prints "meow" to screen
void Cat::Meow()
{
   cout << "Meow.\n";
}

// create a cat, set its age, have it
// meow, tell us its age, then meow again.
int main()
{
  int Age;
  cout<<"How old is Frisky? ";
  cin>>Age;
  Cat Frisky(Age);
  Frisky.Meow();
  cout << "Frisky is a cat who is " ;
  cout << Frisky.GetAge() << " years old.\n";
  Frisky.Meow();
  Age++;
  Frisky.SetAge(Age);
  cout << "Now Frisky is " ;
  cout << Frisky.GetAge() << " years old.\n";
  return 0;
}

3
讨厌在此处使用 get/set,会虐待猫。你不应该能够设置年龄(因为可能会将其设置得更年轻),但是你应该能够增加年龄。 - Martin York
5
应该有一个SetBirthday()函数,然后再有一个GetAge()函数。 - Reunanen
4
由于这是为了学习样例,访问器应该被标记为常量,因为它并不改变对象的内容。"meow" 是一个示例名称,需要根据具体情况进行翻译。 - David Rodríguez - dribeas
如果被复制,你的类会崩溃。 - Neil Kirk
void SetAge(int age); 不是一个访问器而是一个修改器,对吗? - Yeo
显示剩余6条评论

13

即使他是一个学生,也值得尝试回答,因为这是一个复杂的问题,对于C++新访问者来说并不容易 :)

C++中的类服务于两种设计范式的交集,

1)ADT :: 基本上意味着一个新类型,例如整数'int'或实数'double',甚至是像“日期”这样的新概念。在这种情况下,简单的类应该如下所示,

class NewDataType
{
public:
// public area. visible to the 'user' of the new data type.
.
.
.
private:
// no one can see anything in this area except you.
.
.
.
};

这是一个ADT最基本的骨架...当然,你可以忽略公共区域,或者删除访问修饰符(public、private),使整个类型变为私有的。但那只是无意义的,因为NewDataType就变得没用了!可以想象一下一个“int”,你可以声明但不能对它进行任何操作。

因此,你需要一些有用的工具,这些工具基本上并不需要NewDataType存在,但你会使用它们来让你的类型看起来像语言中的任何“原始”类型。

第一个工具是构造函数。在语言的许多地方都需要构造函数。看看int,让我们试着模仿它的行为。

int x; // default constructor.

int y = 5; // copy constructor from a 'literal' or a 'constant value' in simple wrods.
int z = y; // copy constructor. from anther variable, with or without the sametype.
int n(z); // ALMOST EXACTLY THE SAME AS THE ABOVE ONE, it isredundant for 'primitive' types, but really needed for the NewDataType.

以上每一行都是一个声明,变量就在那里被构造。

最后想象一下上面的int变量在一个名为“fun”的函数中,该函数被调用。

int fun()
{
    int y = 5;
    int z = y;
    int m(z);

    return (m + z + y)
    // the magical line.
}

你可以看到这条神奇的语句,在这里你可以告诉编译器任何你想要的! 当你完成每件事情并且你的NewDataType在本地范围内不再有用,比如在函数中,你就需要杀死它。 一个经典的例子就是释放通过 'new' 保留的内存!

所以我们非常简单的 NewDataType 变成了:

class NewDataType
{
public:
// public area. visible to the 'user' of the new data type.
    NewDataType()
    { 
        myValue = new int;
        *myValue = 0;
    }

    NewDataType(int newValue)
    {
        myValue = new int;
        *myValue = newValue;
    }

    NewDataType(const NewDataType& newValue){

        myValue = new int;
        *myValue = newValue.(*myValue);
    }
private:
// no one can see anything in this area except you.
    int* myValue;
};

现在这是非常基本的骨架,要开始构建有用的类,您必须提供公共函数。

在C++中构建类时需要考虑许多微小的工具,

... ... ...

2)Object :: 基本上意味着一个新类型,但不同的是它属于兄弟姐妹、祖先和后代。看看C++中的“double”和“int”,“int”是“double”的子集,因为每个“int”至少在概念上都是一个“double” :)


10
class A
{
  public:
    // a simple constructor, anyone can see this
    A() {}
  protected:
    // a simple destructor. This class can only be deleted by objects that are derived from this class
    // probably also you will be unable to allocate an instance of this on the stack
    // the destructor is virtual, so this class is OK to be used as a base class
    virtual ~A() {}
  private:
    // a function that cannot be seen by anything outside this class
    void foo() {}
};

8
#include <iostream>
#include <string>

class Simple {
public:
  Simple(const std::string& name);
  void greet();
  ~Simple();
private:
  std::string name;
};

Simple::Simple(const std::string& name): name(name) {
  std::cout << "hello " << name << "!" << std::endl;
}

void Simple::greet() {
  std::cout << "hi there " << name << "!" << std::endl;
}

Simple::~Simple() {
  std::cout << "goodbye " << name << "!" << std::endl;
}

int main()
{
  Simple ton("Joe");
  ton.greet();
  return 0;
}

有些傻,但是就是这样。请注意,“visibility”是一个误称:公共和私有控制可访问性,但即使“private”也可以从外部“visible”,只是不可访问(尝试访问它是错误的)。


事实上,可见性可能会引起问题。编译器根据可见性和最佳匹配参数选择要调用的重载函数,并可能得到一个不可访问的函数。这些概念可能会令人困惑。 - David Thornley
为什么Alex使用了string& name而不是string name? - Babiker
2
"const string& name" 表示不进行复制,而 "string name" 告诉编译器要进行复制。当你不需要复制时,为什么要请求一个呢?当你以只读方式使用参数(不是简单值类型,如 int、指针等)时,通过 const 引用传递它们是一个好习惯。 - Alex Martelli

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接