C++中的实例化

5

首先,我想通知您,我已经确切地搜索了有关我的以下问题的答案,但我是C ++的完全新手。

我来自C#和Java的奢华生活,现在试图学习一两个关于c ++的东西

这个问题是关于实例化的。

我使用code :: block作为我的首选IDE。

目前,我只是在尝试用C#(我实际上非常熟悉并且已经编写了几个应用程序)中所谓的内容进行玩耍

2个类

包含主要人员的类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


using Models.Person;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
           Person person = new Person();
           Console.WriteLine(person.getName());
        }
    }
}

以及Person类:

namespace ConsoleApplication1
{
   public class Person{
       private string name = "Bob";

       public string getName(){
           return name;
       }
   }
}

(不用在意语法的好坏,这只是为了模拟我想要实现的内容)

我希望在C++中实现相同的功能。

我查阅了一些资料,学习了头文件的一些知识,并且掌握了一些语法。这是我目前拥有的代码。

main.cpp

#include <iostream>
using namespace std;

int main()
{
   Person person;
   cout << person->getName() << endl;
}

Person.h

#ifndef PERSON_H
#define PERSON_H

#include <string>

class Person
{
    public:
        Person();
        virtual ~Person();
        std::string getName();
    protected:
    private:
};

#endif // PERSON_H

player.cpp

#include "Person.h"
#include <string>

using std::string;

Person::Person()
{
    //ctor
}

Person::~Person()
{
}

string Person::getName()
{
    return name;
}

考虑上述代码,我有多个问题。
我从来没有找到一个好的来源告诉我是否应该使用Person person;实例化还是Person person = new Person();
这两个中哪一个是我应该使用的?
另一个我渴望知道的问题是,我是在头文件还是类文件中定义类成员?
目前我收到以下错误:
“person”未在范围内声明,并且在“person”之前需要“;”。
我并不是要求您解决我的错误,在回答我的问题后,我会自己解决。

7
我来自于C#和Java的奢华生活,现在想了解一些关于C++的东西。请确保你有一本好的C++入门书籍。C++与Java或C#几乎没有任何共同之处。 - James McNellis
阅读完[那本优秀的C++入门书]后,还要考虑阅读C++ FAQ,因为它包含了许多有用的信息。 - tJener
谢谢你分享这些链接 ^^ 我一定会照你的建议找一本 C++ 的书来学习。C++ 是一门非常有趣的编程语言。 - Joey Roosing
8个回答

4
正如larsmans所说,Person personPerson *person = new Person()分别会在堆栈/堆中分配新的Person实例。
这对你来说意味着什么(这也是“我应该使用哪个?”问题的答案),在第一种情况下,对象的内存是自动管理的(这是好消息)。坏消息是对象的生命周期也是自动管理的,你对此没有任何控制。一旦person变量超出作用域,对象就被销毁了。完结撒花。
在第二种情况下(new),对象的生命周期由你管理(直到你执行delete person为止)。这里的坏消息是内存也由你管理:如果你从未执行delete person,那么分配给该对象的内存将泄漏。即使你在作用域中没有任何对person的引用,也不会有任何区别。
因此,如果寿命足够长,则不要使用指针。否则,你必须使用指针。
对于类成员:
  • 它们必须在头文件中声明;如果你不声明并尝试使用它们,你将得到一个编译器错误。
  • 它们可以选择在头文件中定义;如果没有,你可以在.cpp文件中定义它们;如果你不在任何地方定义它们并尝试使用它们,你将得到一个链接器错误。
通常定义应该放在.cpp文件中,但是如果你在头文件中定义非常短的方法也是可以的。 附言 当然,在这个简短的答案中,我没有涉及到很多细节。以下是一些你可能想了解的重要内容:
使用new/delete来管理内存除了可能导致内存泄漏之外,还可能产生许多其他问题;悬空指针(指向已被释放的内存,因此不能再使用--这本质上是与内存泄漏相反的错误)可能是问题清单中排名第二的错误。使用某种类型的智能指针将有助于实现“C#语义”:每当最后一个智能指针指向某个内存时,该内存将自动立即释放(如果它指向一个对象,则析构函数将在此时运行;这称为确定性销毁,C#没有它,必须尝试用IDisposable来模拟它)。在C++0x和Boost中可以使用非常好且成熟的智能指针。在C++中,所有类型都像C#值类型一样工作。如果a占用2MB的内存,并且您执行b = a,则编译器只是用a的副本填充了全新的2MB内存(并且可能要做很多额外的工作来实现这一点)。如果这不是您的意图,则需要存储指向a的指针或引用

为了添加新的/删除,使用智能指针来管理对象生命周期是明智的选择。有许多不同类型的智能指针可供选择,其中一些包括auto_ptr、shared_ptr、unique_ptr、scoped_ptr和weak_ptr。 - Zan Lynx
这是一个非常有趣且易于理解的小教程。非常感谢。它非常清晰易懂! - Joey Roosing
@Joey:感谢你的赞美之词。我添加了一个新部分,其中包含一些对于从C#转到C++的人非常有用的技巧。 - Jon

2
您的 C# 代码的等效代码为:
#include <iostream>
#include <string>

class Person
{
   std::string name;
   public:
     Person() : name("Bob"){}
     std::string getName() { return name; }   
};

int main()
{
   //automatic variable; doesn't need new!
   Person person;
   std::cout << person.getName() << std::endl;

   //pointer ; need to use new
   Person *pPerson = new Person(); //it allocates memory!
   std::cout << pPerson->getName() << std::endl;
   delete pPerson; //it deallocates the memory!
}

输出:

Bob
Bob

请查看在线演示:http://ideone.com/cM0uU

1
谢谢。比这更清晰我无法要求了。特别感谢您提供的示例解释,结合他人的评论非常有帮助。再次感谢。 - Joey Roosing
1
我添加了一个删除操作来清理内存。Joey,要记住C++没有垃圾回收机制,你必须自己清理。 :-) - Daniel T.
@Daniel 或 @Nawaz,当你创建一个指针时,在前面加上 p 是常见的吗?尽管这很合乎逻辑,但我只是好奇。@Daniel T:呵呵,没有垃圾收集器会让人有点不习惯,但经过一些尝试和错误,我会通过艰难的方式或者其他方式适应它的。 :) - Joey Roosing
@Joey:人们通常会写,但这并不是必要的。这只是一种编码风格。 - Nawaz
@Joey:我通常不使用“p”前缀。至于缺失的垃圾回收器,大多数程序员都有一套工具,可以使清理至少半自动化(即智能指针,其中一些正在被纳入C++标准)。 - Daniel T.

2
  1. Person person将在堆栈上构建一个对象。Person *person = new Person将在自由存储器(堆;请注意*,因为您需要一个指针)上构建它。这与C#不同,在C#中,您可以选择structclass之间来获取堆栈或堆分配。在C++中,这些关键字具有不同的含义。如果选择在堆上分配,则必须手动delete该对象。
  2. 在头文件中实现小而性能关键的方法,这些方法不受更改的影响。将其他所有内容放在实现文件中。

不要在头文件中的顶层放置using指令。)


如果您使用默认构造函数创建一个Person对象,则可以省略括号,即为new Person - filmor
2
@filmor:不完全是这样。在这种特殊情况下(因为该类具有默认构造函数),两种语法完全相同。或者我漏掉了什么? - David Rodríguez - dribeas
在头文件的顶层不要使用using语句?为什么?顺便感谢您留下答案 ^^ 您的评论与Nawaz的答案很相似。 - Joey Roosing
@Joey:因为它会污染任何直接或间接包含该头文件的模块的命名空间,随着程序规模和复杂度的增长,这可能会导致一些意外情况。#include只是文本替换:头文件的整个内容将填充到#include所在的位置。 - Fred Foo
啊,这就解释了。我从这个单一的线程中学到了很多东西。它确实是一门非常棒的语言,可以让你成功,也可以让你轻易地失败。哈哈,这也是我所了解到的。尽管如此,我真的很喜欢学习它。谢谢 :) - Joey Roosing

2
Person的主要区别在于你是想要值语义还是引用语义---对于实体对象,引用语义是必须的(因为它们保留了身份),但对于大多数其他对象,值语义更可取。在你的例子中,Person可以轻松地使用值语义,但在更现实的情况下,这将取决于类在应用程序中的角色。
无论如何,如果你定义了值语义,你就不需要(几乎从不)动态分配实例。你将它们定义为本地变量,根据需要进行复制,并让编译器处理其余部分。你还要确保复制、赋值和析构函数执行你想要的操作---在现代C++中,通常情况下都是这样的,因为应用程序级别的对象将基于具有复制构造函数、赋值和销毁函数的低级对象,这些函数会执行正确的操作,但这不是过去的情况,也不总是现在的情况。如果你想要实体语义,你必须决定实体的生命周期应该是什么。(我会像在其他语言中一样说,但许多Java和C#程序员忘记了这一点。而且往往能够逃脱。在C++中,你永远无法逃脱。)实体对象通常使用new创建,并在它们的生命周期结束时必须被销毁。
在C++中,禁止复制和赋值实体对象(除了可能提供克隆函数)也是一个好习惯。
关于第二点:完整的类定义必须在一个地方;一旦你用最后的大括号关闭了它,就不能重新打开定义。这意味着成员函数声明(但不是定义)和成员数据必须出现在类定义中,在头文件中。然而,最好尽可能少地放在头文件中,函数的定义(实现)通常属于源文件。
(所有这些规则都有例外情况,但它们是一个很好的起点。)

1
你得到的错误是因为没有使用 #include "Player.h"(假设这是包含 class Person 声明的头文件的名称,因为你的 #define PLAYER_H 暗示了这一点)。之后,它会抱怨你使用了 person->getName() 而不是 person.getName()-> 用于指针,你可能需要再学习一下这方面的知识。
至于定义类成员,如果我理解术语正确的话,你应该在头文件中进行 声明。然后,在类文件中的构造函数中通常会隐式地进行 定义
class MyClass {
  int myVar;  // Declare the variable
public:
  MyClass(int inVar);  // Declare the constructor function
};

然后在类文件中:

MyClass::MyClass(int inVar)  // Define the constructor function
: myVar(inVar)     // Define the variable
{
}

当然,有时候你可以在头文件中同时进行这两个操作,这是合适的。

class MyClass {
  int myVar;  // Declare the variable
public:
  MyClass(int inVar) // Declare and define the constructor function
  : myVar(inVar)
  {}
};

啊,抱歉,我忘记同时将它们更改为“Person”。我正在更改我的个人示例,似乎有些马虎。这样做的原因是我想使用与C#中相同的例子,但使用C++编写。我现在已经更改了它们,并且确实使其正常工作。谢谢您的提醒!我也会在我的帖子中进行更改。这可能是一个愚蠢的初学者错误,呵呵。 - Joey Roosing

1
main.cpp 的开头,你还必须加入 #include "Person.h"

1
在Java中,一切皆为引用类型,因此必须使用new进行实例化,它将创建一个值类型并返回对其的引用。在C++中,基本类型是值类型,类似于Java的引用类型看起来像Person* person; 当你转到C++时,应该放弃你从Java中了解到的一切,并从基础开始学习,否则从高级到低级编程可能会非常令人困惑。

1

如果你是从C#过来的,这个解释可能会有所帮助:

在C++中,类型的声明/定义并不表明它是“值类型”还是“引用类型”。'struct'和'class'之间唯一的区别是,在'class'中成员默认为私有。它的使用方式决定了它的类型。

当你声明一个Person时,你创建的是一个“值类型”。 当你声明一个Person*时,你创建了一个指向Person的指针(在C#中是引用类型),通常使用new在堆上分配内存。在C++中,引用(即Person&)类似于C#中的ref Person

正如James在他的评论中指出的那样,获取一本好的C++书籍是你最好的选择。


我一定会买那本书。C++社区似乎非常乐于助人,这更激励我在C++方面变得更好,并且以后能够自己回答一些问题! :) 顺便说一句,我喜欢你解释C#等效性的方式。谢谢。 - Joey Roosing

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